24

I need to detect an orientation change in my application, but I don't want my layout to be changed from portrait to landscape. Currently I'm using the OrientationEventListener however detecting the orientation angle is not enough. I want to detect that the user changed from portrait to landscape or viceversa, and that is not just detecting if the orientation angle is 90 or 270.

I want to do the same detection that the Android does to change the activity's orientantion. I tried overriding onConfigurationChanged and check if orientantion is landscape/portrait, however this still changes my activity layout to landscape.

Is there a way to use onConfigurationChanged but force the layout to stay in portrait?
Is there another way to detect orientantion change without using OrientationEventListener. Ultimately I can implement my own orientation changed algorithm, any ideas on this? It has to be something more complex than if(90-THRESHOLD <= orientation <= 90+THRESHOLD), I want to detect if the user made the complete movement Portrait->Landscape or Landscape->Portrait.

Thanks for the help,
Filipe

ffleandro
  • 4,039
  • 4
  • 33
  • 48
  • 2
    Ack, sorry, had a brain fart on what you really needed. Far as I know, you cannot use `onConfigurationChanged()` unless you actually allow the configuration to change (meaning the view has to rotate in this case). There's no way to intercept the event before it is dispatched to the window system. – devunwired Nov 23 '11 at 23:14

5 Answers5

37

Ok, after trying to use the Android API and not being able to do what I need, I implemented my own algorithm and actually it wasn't that complicated: I used a OrientationEventListener, and calculated if the orientation is in the 4 orientation points (in my code I only detect LANDSCAPE_RIGHT and PORTRAIT_UP:

orientationListener = new OrientationEventListener(context, SensorManager.SENSOR_DELAY_UI) {
        public void onOrientationChanged(int orientation) {
            if(canShow(orientation)){
                show();
            } else if(canDismiss(orientation)){
                dismiss();
            }
        }
    };

@Override
public void onResume(){
    super.onResume();
    orientationListener.enable();
}

@Override
public void onPause(){
    super.onPause();
    orientationListener.disable();
}

private boolean isLandscape(int orientation){
        return orientation >= (90 - THRESHOLD) && orientation <= (90 + THRESHOLD);
    }

private boolean isPortrait(int orientation){
    return (orientation >= (360 - THRESHOLD) && orientation <= 360) || (orientation >= 0 && orientation <= THRESHOLD);
}

public boolean canShow(int orientation){
    return !visible && isLandscape(orientation);
}

public boolean canDismiss(int orientation){
    return visible && !dismissing && isPortrait(orientation);
}
ffleandro
  • 4,039
  • 4
  • 33
  • 48
  • Ok, so now I have another problem. The view that I want to show in top of my activity view has to be in landscape mode. How can I force a view to be in landscape without forcing the application to also do it? – ffleandro Nov 24 '11 at 21:25
  • I'm having a issue which seems to be close to yours. What do the show() and dismiss() methods do? When I try your example I get a runtime exception: ERROR/AndroidRuntime(7868): FATAL EXCEPTION: main android.app.SuperNotCalledException: Activity Orientation did not call through to super.onConfigurationChanged() – leshka Sep 25 '12 at 17:29
  • but if I call to super at the beginning of the method - all the orientations (incl. those that I don't want to support) being shown – leshka Sep 25 '12 at 17:30
  • What is the `THRESHOLD` value in this case and why we need it.. i am also stuck in this case and trying to use this code, to get the current orientation. – User7723337 Aug 06 '13 at 13:26
  • For instance, if you rotate your device very quickly, you may pass directly from 85º degrees to 92º degrees, without dispatching an event exactly at 90º. The Threshold is a value you use to detect if it is almost near a specific angle that you can round to it. Try different values, I think I used 15 ou 30. – ffleandro Sep 10 '13 at 00:20
11

Hi screamingnoises is this what you`re looking for?

// Set background image, rotatable
    View view = getWindow().getDecorView(); 
    int orientation = getResources().getConfiguration().orientation; 
    if (Configuration.ORIENTATION_LANDSCAPE == orientation) { 
        //Do SomeThing; // Landscape
    } else { 
        //Do SomeThing;  // Portrait
    } 
Lars
  • 915
  • 4
  • 18
  • 27
  • Yes, this worked. Thanks. I would rather use the onConfigurationChanged method, because with your solution I still need a OrientationEventListener to check the orientation. I will wait Devunwired to reply to see if he has a solution. Otherwise I'll mark yours as accepted ;) – ffleandro Nov 23 '11 at 22:28
  • My answer won't work, so I pulled it. Although, I fear this will suffer from the same issue. If you don't allow the configuration to actually change from portrait to landscape, the Resources will never report anything other than portrait regardless of the device orientation. – devunwired Nov 23 '11 at 23:16
  • Actually this didn't work. If I lock the activity to portrait mode using the attribute screenOrientation="portrait" in the manifest, this doesn't work. This only tells what the screen orientation is, and not the detection of the screen orientation change. – ffleandro Nov 23 '11 at 23:25
  • Nice idea mate, thanks! This is exactly what I needed ! – narancs Feb 20 '17 at 16:05
8

I created the following class to detect orientation changes, keeping my activity original orientation:

public class SensorOrientationChangeNotifier {

    public final String TAG = getClass().getSimpleName();
    private ArrayList<WeakReference<SensorOrientationChangeNotifier.Listener>> mListeners = new ArrayList<WeakReference<SensorOrientationChangeNotifier.Listener>>(3);

    private int mOrientation = 0;
    private SensorEventListener mSensorEventListener;
    private SensorManager mSensorManager;

    private static SensorOrientationChangeNotifier mInstance;

    public static SensorOrientationChangeNotifier getInstance() {
        if (mInstance == null)
            mInstance = new SensorOrientationChangeNotifier();

        return mInstance;
    }

    private SensorOrientationChangeNotifier() {
        mSensorEventListener = new NotifierSensorEventListener();
        Context applicationContext = GlobalData.getInstance().getContext();
        mSensorManager = (SensorManager) applicationContext.getSystemService(Context.SENSOR_SERVICE);

    }

    /**
     * Call on activity reset()
     */
    private void onResume() {
        mSensorManager.registerListener(mSensorEventListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
    }

    /**
     * Call on activity onPause()
     */
    private void onPause() {
        mSensorManager.unregisterListener(mSensorEventListener);
    }

    private class NotifierSensorEventListener implements SensorEventListener {

        @Override
        public void onSensorChanged(SensorEvent event) {
            float x = event.values[0];
            float y = event.values[1];
            int newOrientation = mOrientation;
            if (x < 5 && x > -5 && y > 5)
                newOrientation = 0;
            else if (x < -5 && y < 5 && y > -5)
                newOrientation = 90;
            else if (x < 5 && x > -5 && y < -5)
                newOrientation = 180;
            else if (x > 5 && y < 5 && y > -5)
                newOrientation = 270;

            //Log.e(TAG,"mOrientation="+mOrientation+"   ["+event.values[0]+","+event.values[1]+","+event.values[2]+"]");
            if (mOrientation != newOrientation){
                mOrientation = newOrientation;
                notifyListeners();
            }

        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }

    }

    public int getOrientation() {
        return mOrientation;
    }

    public interface Listener {
        void onOrientationChange(int orientation);
    }

    public void addListener(SensorOrientationChangeNotifier.Listener listener) {
        if (get(listener) == null) // prevent duplications
            mListeners.add(new WeakReference<SensorOrientationChangeNotifier.Listener>(listener));

        if (mListeners.size() == 1) {
            onResume(); // this is the first client
        }
    }

    public void remove(SensorOrientationChangeNotifier.Listener listener) {
        WeakReference<SensorOrientationChangeNotifier.Listener> listenerWR = get(listener);
        remove(listenerWR);
    }

    private void remove(WeakReference<SensorOrientationChangeNotifier.Listener> listenerWR) {
        if (listenerWR != null)
            mListeners.remove(listenerWR);

        if (mListeners.size() == 0) {
            onPause();
        }

    }

    private WeakReference<SensorOrientationChangeNotifier.Listener> get(SensorOrientationChangeNotifier.Listener listener) {
        for (WeakReference<SensorOrientationChangeNotifier.Listener> existingListener : mListeners)
            if (existingListener.get() == listener)
                return existingListener;
        return null;
    }

    private void notifyListeners() {
        ArrayList<WeakReference<SensorOrientationChangeNotifier.Listener>> deadLiksArr = new ArrayList<WeakReference<SensorOrientationChangeNotifier.Listener>>();
        for (WeakReference<SensorOrientationChangeNotifier.Listener> wr : mListeners) {
            if (wr.get() == null)
                deadLiksArr.add(wr);
            else
                wr.get().onOrientationChange(mOrientation);
        }

        // remove dead references 
        for (WeakReference<SensorOrientationChangeNotifier.Listener> wr : deadLiksArr) {
            mListeners.remove(wr);
        }
    }

    public boolean isPortrait(){
        return mOrientation == 0 || mOrientation == 180;
    }

    public boolean isLandscape(){
        return !isPortrait();
    }
}

Use it as follows:

In AndroidManifest.xml -

    <activity
        ...
        android:screenOrientation="portrait"
        >

In your activity:

public class MainActivity extends Activity implements  SensorOrientationChangeNotifier.Listener {

    @Override
    protected void onResume() {
        super.onResume();

        SensorOrientationChangeNotifier.getInstance().addListener(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        SensorOrientationChangeNotifier.getInstance().remove(this);
    }

    @Override
    public void onOrientationChange(int orientation) {
            if (orientation == 90 || orientation == 270){
                 // Do some landscape stuff
                } else {
                 // Do some portrait stuff
                 }
        }
    }
}
Asaf Pinhassi
  • 15,177
  • 12
  • 106
  • 130
1

A kotlin version of the Asaf Pinhassi answer.

import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import java.lang.ref.WeakReference
import java.util.*

class SensorOrientationChangeNotifier private constructor(context: Context) {
val TAG = javaClass.simpleName
private val mListeners = ArrayList<WeakReference<Listener?>>(3)
var orientation = 0
    private set
private val mSensorEventListener: SensorEventListener
private val mSensorManager: SensorManager

/**
 * Call on activity reset()
 */
private fun onResume() {
    mSensorManager.registerListener(mSensorEventListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL)
}

/**
 * Call on activity onPause()
 */
private fun onPause() {
    mSensorManager.unregisterListener(mSensorEventListener)
}

private inner class NotifierSensorEventListener : SensorEventListener {
    override fun onSensorChanged(event: SensorEvent) {
        val x = event.values[0]
        val y = event.values[1]
        var newOrientation: Int = orientation
        if (x < 5 && x > -5 && y > 5) newOrientation = 0 else if (x < -5 && y < 5 && y > -5) newOrientation = 90 else if (x < 5 && x > -5 && y < -5) newOrientation = 180 else if (x > 5 && y < 5 && y > -5) newOrientation = 270

        //Log.e(TAG,"mOrientation="+mOrientation+"   ["+event.values[0]+","+event.values[1]+","+event.values[2]+"]");
        if (orientation != newOrientation) {
            orientation = newOrientation
            notifyListeners()
        }
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}

interface Listener {
    fun onOrientationChange(orientation: Int)
}

fun addListener(listener: Listener) {
    if (get(listener) == null) // prevent duplications
        mListeners.add(WeakReference(listener))
    if (mListeners.size == 1) {
        onResume() // this is the first client
    }
}

fun remove(listener: Listener) {
    val listenerWR = get(listener)
    remove(listenerWR)
}

private fun remove(listenerWR: WeakReference<Listener?>?) {
    if (listenerWR != null) mListeners.remove(listenerWR)
    if (mListeners.size == 0) {
        onPause()
    }
}

private operator fun get(listener: Listener): WeakReference<Listener?>? {
    for (existingListener in mListeners) if (existingListener.get() === listener) return existingListener
    return null
}

private fun notifyListeners() {
    val deadLiksArr = ArrayList<WeakReference<Listener?>>()
    for (wr in mListeners) {
        if (wr.get() == null) deadLiksArr.add(wr) else wr.get()!!.onOrientationChange(orientation)
    }

    // remove dead references
    for (wr in deadLiksArr) {
        mListeners.remove(wr)
    }
}

val isPortrait: Boolean
    get() = orientation == 0 || orientation == 180

val isLandscape: Boolean
    get() = !isPortrait

companion object {
    private var mInstance: SensorOrientationChangeNotifier? = null
    fun getInstance(context: Context): SensorOrientationChangeNotifier? {
        if (mInstance == null) mInstance = SensorOrientationChangeNotifier(context)
        return mInstance
    }
}

init {
    mSensorEventListener = NotifierSensorEventListener()
    mSensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
}
}
Dishonered
  • 8,449
  • 9
  • 37
  • 50
-3

To detect orientation, we will ask the activity to provide us with width and height of the activity, so we can find if Width > Height which will be "Landscape" or the opposite Height > width which is "Portrait" - The Following Code is tested and Working.

@SuppressWarnings("deprecation")
public void detectScreenOrientation(){
WindowManager wm = getWindowManager();
Display d = wm.getDefaultDisplay();
if (d.getWidth() > d.getHeight()){
    Log.d("Orientation","Landscape mode");
}
else {
    Log.d("Orientation", "Portrait mode");
}
}
Bahi Hussein
  • 433
  • 5
  • 10