Joe Maher
Joe Maher

Reputation: 5460

Android - 'sensorPortrait' orientation not working

I have come across a problem where the orientation sensorPortrait does not work, i have tried enabling both through the manifest and within the activity itself with

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);

But this seems to just be locked in normal portrait mode, however if i try `fullSensor'

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);

which according to the docs

The orientation is determined by the device orientation sensor for any of the 4 orientations. This is similar to "sensor" except this allows any of the 4 possible screen orientations, regardless of what the device will normally do (for example, some devices won't normally use reverse portrait or reverse landscape, but this enables those). Added in API level 9.

and it does exactly that, all 4 orientations are possible. If i also try

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);

I am able to achieve reverse portrait, which leads me back to my original question, why does sensorPortrait not work? It looks like it has something to do with this line from the docs for `fullSensor'

regardless of what the device will normally do (for example, some devices won't normally use reverse portrait or reverse landscape, but this enables those)

So how do i enable it, is that possible and why does fullSensor seem to override it but not sensorPortrait? I can't seem to find any mention of how to do so. This question suggests that the PhoneWindowManager is responsible for this.

Is the ideal solution just to create an OrientationEventListener() and manually call setRequestedOrientation() manually depending on the values returned via onOrientationChanged(int orientation)?

Upvotes: 12

Views: 3578

Answers (2)

Lucid Dev Team
Lucid Dev Team

Reputation: 641

Here is an updated class from the selected answer that you can extend from. It supports switching between:

setPortraitSensorMode()

setLandscapeSensorMode()

setAllSensorModes()

Below is the full class:

public class SensorOrientationActivity extends AppCompatActivity {

    private static final String TAG = "SensorOrientation";

    private OrientationEventListener mOrientationListener;
    private OrientationChangeListener mListener;
    private CurrentOrientation mCurrentOrientation = CurrentOrientation.PORTRAIT;
    private SensorMode currentSensorMode = SensorMode.PORTRAIT_SENSOR;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mOrientationListener = new OrientationEventListener(this) {
            @Override
            public void onOrientationChanged(int degrees) {
                if (degrees != OrientationEventListener.ORIENTATION_UNKNOWN) {
                    orientationChanged(degrees);
                }
            }
        };

        mOrientationListener.enable();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mOrientationListener.enable();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mOrientationListener.disable();
    }

    public void setOrientationChangeListener(OrientationChangeListener listener) {
        mListener = listener;
    }

    public void setPortraitSensorMode() {
        currentSensorMode = SensorMode.PORTRAIT_SENSOR;
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);  // Let the OrientationEventListener handle it
        Log.d(TAG, "Portrait Sensor Mode Set");
    }

    public void setLandscapeSensorMode() {
        currentSensorMode = SensorMode.LANDSCAPE_SENSOR;
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);  // Let the OrientationEventListener handle it
        Log.d(TAG, "Landscape Sensor Mode Set");
    }

    public void setAllSensorModes() {
        currentSensorMode = SensorMode.ALL_SENSOR;
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        Log.d(TAG, "All Sensor Modes Set");
    }

    private void orientationChanged(int degrees) {
        //Log.d(TAG, "Orientation changed: " + degrees);
        switch (currentSensorMode) {
            case ALL_SENSOR:
                handleAllSensorModes(degrees);
                break;
            case PORTRAIT_SENSOR:
                handlePortraitSensorMode(degrees);
                break;
            case LANDSCAPE_SENSOR:
                handleLandscapeSensorMode(degrees);
                break;
        }
    }

    private void handleAllSensorModes(int degrees) {
        updateCurrentOrientation(degrees);
    }

    private void handlePortraitSensorMode(int degrees) {
        if (isPortrait(degrees) || isReversePortrait(degrees)) {
            updateCurrentOrientation(degrees);
        }
    }

    private void handleLandscapeSensorMode(int degrees) {
        if (isLandscape(degrees) || isReverseLandscape(degrees)) {
            updateCurrentOrientation(degrees);
        }
    }

    private void updateCurrentOrientation(int degrees) {
        CurrentOrientation newOrientation = null;

        if (isPortrait(degrees)) {
            newOrientation = CurrentOrientation.PORTRAIT;
        } else if (isReversePortrait(degrees)) {
            newOrientation = CurrentOrientation.REVERSE_PORTRAIT;
        } else if (isLandscape(degrees)) {
            newOrientation = CurrentOrientation.LANDSCAPE;
        } else if (isReverseLandscape(degrees)) {
            newOrientation = CurrentOrientation.REVERSE_LANDSCAPE;
        }

        if (newOrientation != null && newOrientation != mCurrentOrientation) {
            mCurrentOrientation = newOrientation;
            applyOrientation(newOrientation);
            notifyListener();
        }
    }

    private void applyOrientation(CurrentOrientation orientation) {
        switch (orientation) {
            case PORTRAIT:
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                Log.d(TAG, "Orientation set to PORTRAIT");
                break;
            case REVERSE_PORTRAIT:
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                Log.d(TAG, "Orientation set to REVERSE_PORTRAIT");
                break;
            case LANDSCAPE:
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
                Log.d(TAG, "Orientation set to LANDSCAPE");
                break;
            case REVERSE_LANDSCAPE:
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
                Log.d(TAG, "Orientation set to REVERSE_LANDSCAPE");
                break;
        }
    }

    private boolean isPortrait(int degrees) {
        return (degrees >= 315 || degrees <= 45);
    }

    private boolean isReversePortrait(int degrees) {
        return (degrees >= 135 && degrees <= 225);
    }

    private boolean isLandscape(int degrees) {
        return (degrees > 45 && degrees < 135);
    }

    private boolean isReverseLandscape(int degrees) {
        return (degrees > 225 && degrees < 315);
    }

    private void notifyListener() {
        if (mListener == null) return;
        switch (mCurrentOrientation) {
            case PORTRAIT:
                mListener.onPortrait();
                break;
            case REVERSE_PORTRAIT:
                mListener.onReversePortrait();
                break;
            case LANDSCAPE:
                mListener.onLandscape();
                break;
            case REVERSE_LANDSCAPE:
                mListener.onReverseLandscape();
                break;
        }
    }

    interface OrientationChangeListener {
        void onPortrait();
        void onReversePortrait();
        void onLandscape();
        void onReverseLandscape();
    }

    enum CurrentOrientation {
        PORTRAIT, REVERSE_PORTRAIT, LANDSCAPE, REVERSE_LANDSCAPE
    }

    enum SensorMode {
        ALL_SENSOR, PORTRAIT_SENSOR, LANDSCAPE_SENSOR
    }
}

Upvotes: 0

Joe Maher
Joe Maher

Reputation: 5460

As a work around i have created a SensorPortraitActivity:

public class SensorPortraitActivity extends AppCompatActivity {

    private static final int PORTRAIT = 0;
    private static final int REVERSE_PORTRAIT = 180;
    private static final int OFFSET = 45;
    private static final int UNKNOWN = -1;

//  account for 0 = 360 (eg. -1 = 359)
    private static final int PORTRAIT_START = PORTRAIT - OFFSET + 360; 
    private static final int PORTRAIT_END = PORTRAIT + OFFSET;
    private static final int REVERSE_PORTRAIT_START = REVERSE_PORTRAIT - OFFSET;
    private static final int REVERSE_PORTRAIT_END = REVERSE_PORTRAIT + OFFSET;

    private OrientationChangeListener mListener;
    private OrientationEventListener mOrientationListener;
    private CurrentOrientation mCurrentOrientation;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mOrientationListener = new OrientationEventListener(this) {
            @Override
            public void onOrientationChanged(int i) {
                orientationChanged(i);
            }
        };
    }

    @Override
    protected void onResume() {
        super.onResume();
        mOrientationListener.enable();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mOrientationListener.disable();
    }

    //optional 
    public void setOrientationChangeListener(OrientationChangeListener listener){
        mListener = listener;
    }

    private void orientationChanged(int degrees) {

        if (degrees != UNKNOWN){

            if (degrees >= PORTRAIT_START || degrees <= PORTRAIT_END){

                if (mCurrentOrientation != CurrentOrientation.PORTRAIT){

                    mCurrentOrientation = CurrentOrientation.PORTRAIT;
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

                if (mListener != null){
                    mListener.onPortrait();
                }
            }
        } else if (degrees >= REVERSE_PORTRAIT_START && degrees <= REVERSE_PORTRAIT_END){

            if (mCurrentOrientation != CurrentOrientation.REVERSE_PORTRAIT){

                mCurrentOrientation = CurrentOrientation.REVERSE_PORTRAIT;
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);

                    if (mListener != null) {
                        mListener.onReversePortrait();
                    }
                }
            }
        }
    }

    interface OrientationChangeListener {
        void onPortrait();
        void onReversePortrait();
    }

    enum CurrentOrientation {
        PORTRAIT, REVERSE_PORTRAIT
    }
}

Although it does seem like overkill for something as simple as this.

To use it simple extend SensorPortraitActivity

public class ExampleActivity extends SensorPortraitActivity implements SensorPortraitView.OrientationChangeListener {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //set listener if you want callbacks
        super.setOrientationChangeListener(this);
    }

    @Override
    public void onPortrait() {
        //portrait orientation
    }

    @Override
    public void onReversePortrait() {
        //reverse portrait orientation
    }
}

Upvotes: 12

Related Questions