6

I have an app for Android, I want to force tablet (sw600dp and higher) to display in Landscape mode and phone to display in Portrait mode, so I handle in onCreate of my BaseActivity class

boolean isTablet= getResources().getBoolean(R.bool.isTablet);
if (isTablet) {
     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else { 
     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} 

And the way I put "isTablet" in bools.xml file and put it in

values folder for phone

   <resources>
        <bool name="isTablet">false</bool>
   </resources>

values-sw600dp for tablet

   <resources>
        <bool name="isTablet">true</bool>
   </resources>

And in AndroidManifest I use

android:screenOrientation="nosensor"

just ensure to disable device orientation's sensor.

It seems to be my approach works fine (Landscape for tablet and Portrait for phone) BUT the problem happens when I run my app on Nexus 7 - my activities create twice. These steps are:

  1. Hole Nexus 7 in Portrait (it's fine if I hold tablet in Landscape)
  2. Then run the app

I find that the problem is the method setRequestedOrientation() (with 2 steps above). So I don't want to call that method anymore. I try to set orientation in AndroidManifest like:

android:screenOrientation="@integer/orientation"

Because: SCREEN_ORIENTATION_LANDSCAPE = 0 SCREEN_ORIENTATION_PORTRAIT = 1

I declare "orientation" in integers.xml file and put it in

values folder for phone

<resources>
    <integer name="orientation">1</integer>
</resources>

values-sw600dp for tablet

<resources>
    <integer name="orientation">0</integer>
</resources>

Again, my approach tends to works fine BUT I find that AndroidMenifest just understands "orientation" in values folder, not in values-sw600dp folder. I don't want my activities to call 2 times. Did you have a problem like that?? Could you solve it? Thanks.

Tan Tran
  • 166
  • 1
  • 6
  • 1
    Quote from lint : Resources referenced from the manifest cannot vary by configuration (except for version qualifiers, e.g. -v21.) Had the same problem, seems we have to use dynamic orientation changes, but it causes activity restarts. – Renaud Favier Jul 25 '17 at 10:14

3 Answers3

1

From the docs setRequestedOrientation(int):

If the activity is currently in the foreground or otherwise impacting the screen orientation, the screen will immediately be changed (possibly causing the activity to be restarted)

It's not being re-created, it's just being restarted to account for the change of orientation.

So just move the functionality that shouldn't be run twice to onCreate and you're done.

Simas
  • 43,548
  • 10
  • 88
  • 116
  • "possibly causing the activity to be restarted" As you said that the system didn't call onCreate(), But I wrote a Log in onCreate() method and it printed 2 times. – Tan Tran Oct 25 '14 at 17:05
  • @Peter which Nexus 7 are you using 2012? – Simas Oct 25 '14 at 17:12
  • I'm using Nexus 7 2013 (Android 4.4) and another device Samsung 7 inch as well, both devices had the same problem. – Tan Tran Oct 26 '14 at 01:50
  • @Peter tried with nexus 7 2013. Your described behaviour wasn't present. – Simas Oct 26 '14 at 06:40
  • you means you used Nexus 7 to test the problems and you didn't see it right? It's so strange, I tried many times and the problems is still there, by the way thanks for your helping. I'll create a simple demo to test that problem again. – Tan Tran Oct 26 '14 at 11:00
  • Hey, I have followed the EXACT implementation you described, and I face the same issue. To be more clear: I have used a Nexus 7 2013 and a Moto G 4g, and observed the following: in Nexus 7, I have enabled auto-rotation from the device Settings, and even when I start my app when the device is in Portrait mode, the screen is switched to landscape and the activity is NOT restarted twice. However, in my Moto G, I have disabled auto-rotation from the device Settings (hence always in portrait mode), and when I start the app, the screen switches to landscape, however the activity DOES start twice! – Dimitris Oct 31 '14 at 20:28
  • Same problem here, has a solution been found yet ? – Renaud Favier Jul 25 '17 at 08:52
  • @RenaudFavier I stopped finding the solution, instead tried to live with it. It's a rare and special case which is device issue properly, so depends on your app, you can try to handle when the activity get restarted, read this https://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent), it might help. – Tan Tran Jul 28 '17 at 10:44
  • @Peter same here, I found a way to live with it, but it feels wrong. – Renaud Favier Jul 28 '17 at 14:27
1

Simas answer is sort of correct, let me explain.

Let's say you are holding your device in a portrait position, you then start your Activity and in onCreate you call the following:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

Then, you are basically telling the device it should rotate (since you are holding the device in portrait mode).

But if you hold the device in landscape before launching your Activity and call the same as above, your device doesn't need to rotate since it is already in landscape mode.

As we all know, rotating your device causes the Activity to be destroyed and created again. The same goes for when calling setRequestedOrientation (if the device is currently not in the requested position).


So.. How to deal with it.

  • The first and most obvious option would be to not call setRequestedOrientation. Instead handle your rotation by using onSaveInstanceState to store your values and onRestoreInstanceState to restore the values that were stored in bundle, etc..
  • In my case, because of the design of my application, I had to use setRequestedOrientation because I'm rotating the device based on the selected video dimensions.

    Here is how I accomplished this.

    Since I want to handle rotation myself, I first had to add the following in my manifest:

    android:configChanges="orientation|screenSize|keyboardHidden"
    

    By doing this you are overriding configuration changes. As mentioned in the docs - This technique should be considered a last resort

    Next, I created a different layout for when I want to rotate the device to landscape and called the following:

    private void rotateScreen(Uri vidUri) {
        try {
            MediaMetadataRetriever retriever = new MediaMetadataRetriever();
            Bitmap bmp;
            retriever.setDataSource(this, vidUri);
            bmp = retriever.getFrameAtTime();
    
            videoWidth = bmp.getWidth();
            videoHeight = bmp.getHeight();
    
            if (videoWidth > videoHeight) {
                setContentView(R.layout.activity_video_player_land);
                this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
            if (videoWidth < videoHeight) {
                setContentView(R.layout.activity_video_player);
                this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            }
    
        } catch (RuntimeException ex) {
            Log.e("MediaMetadataRetriever", "- Failed to rotate the screen");
    
        }
    }
    

    Since everything in my portrait layout is in my landscape layout, I do not have to worry about resources not being found.


The solution I went with will not work for all applications (if you have different resources for different orientations) but in my case, I use all the same resource, resized and moved around to better fit the landscape view.

HB.
  • 4,116
  • 4
  • 29
  • 53
0

In my case My screen orientation in manifest was portrait. I have changed it to landscape

android:screenOrientation="landscape" 

then remove the line

this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);

from the onCreate block This solved my problem. Sometimes the answer might be that easy

smoothumut
  • 3,423
  • 1
  • 25
  • 35