April 2nd, 2020

Orientation, Spanning and Insets

Meir Ben Itay
Principal SW Engineer

Hello Microsoft Surface Duo Developers!

Until recently, mobile devices all had one thing in common: a single screen. While applications had to support multiple screen sizes, recently manufacturers got creative and introduced new foldable, dual-screen devices, Google added support for multi-windowing on Android. Now applications need to dynamically handle size changes and various screen sizes.

In today’s blog post, I’ll discuss how your Android application will behave on the Surface Duo, by answering the following questions:

  1. Is the activity spanned or running in a single screen?
  2. If a single screen, is it on the right or left portrait or top or bottom landscape
  3. Is there an inset from the keyboard?
  4. How to launch an external activity while spanned, without it taking the whole display

We also published a new sample called Orientation And Spanning in our GitHub repository.

Spanning and Screen Location (questions 1 and 2)

To best support dual-screen devices, activities should support resizing and react to configuration changes, to do that, add these lines to the activity manifest:

android:resizeableActivity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"

This also means that the activity will not restart every time its size changes.

On Surface Duo the following actions will fire configuration change:

  • Orientation change – the aspect ratio of the app changes
  • Span or un-span – when the user drags the app to span both displays horizontally or vertically

Next, override the onConfigurationChanged method in the activity,

In it, gather the information we need:

  • From the orientation field, find out if the activity is in Landscape or portrait,
  • Detect if the user spanned the activity by using the ScreenHelper.isDualMode helper method
  • Then use the getLocationOnScreen to find out the absolute coordinates of our window.
@Override
public void onConfigurationChanged (Configuration newConfig){
 	super.onConfigurationChanged(newConfig);
boolean isSpanned = ScreenHelper.isDualMode(MainActivity.this);

	int screenPosition[] = new int [2];
	getWindow().getDecorView().getLocationOnScreen(screenPosition);
}

Using this information, I can answer question 1 and 2:

Spanned Not Spanned
Landscape Application spanned across 2 screens
Application in landscape mode on the top screen Application in landscape mode on the bottom screen
Portrait Application spanned in portrarit mode
Application in portrait mode on the left screen
Application in portrait mode on the right screen

Table: 6 different configuration an activity can be

When run on the emulator, you’ll notice the sample uses animations to re-arrange the user interface when configuration changes are detected.

Screen position cannot be determined

This blog was written with pre-release implementations for some methods including getLocationOnScreen – some changes were made prior to shipping and the screen position can no longer be determined from this API alone.

Detecting when the activity is moved from screen to screen

The onConfigurationChanged method will let us know on size changes on our activity, but it will not notify if the user moves the activity from the side to side or top to bottom screens.

Detecting that is a little bit more work:

  1. First, find the root view for our activity like this:
View rootView = getWindow().getDecorView().getRootView();
  1. Add a layout listener using the addOnLayoutChangeListener The listener will fire whenever the user moves the activity.

Keyboard inset

To detect the keyboard inset, you first add android:windowSoftInputMode to your activity manifest:

android:windowSoftInputMode="stateAlwaysHidden|adjustResize"

(the adjustResize part is the relevant one) Then set a listener with the setOnApplyWindowInsetsListener method on the root view of the activity. Android will call the listener whenever the keyboard appears and the systemWindowInsets.bottom will have the pixel amount taken by the keyboard even if the keyboard is floating. Emulator with keyboard open

Figure: keyboard inset 702 pixels

Launching external activity while spanned, without it taking the whole display

Whenever an activity launched another activity within the same Task, the operating system will configure the activity to open on the same configuration of the original one as those activities will occupy the same window. Meaning if our activity is spanned and we will open another, the activity will be spanned as well.

This might work well for some scenarios but on others we might want that our activity and the new one will appear side by side.

To achieve this, we can use the same mechanism of launching the activity in a new task like we did in the sample and explained in the Bring your app to Surface Duo – Step 2 blog post.

intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK| Intent.FLAG_ACTIVITY_NEW_TASK);

Use the Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK flags when launching the activity.

This will cause the spanned activity to un-span and the current activity to be on the adjacent screen.

Try out the Orientation And Spanning sample on GitHub on the Surface Duo emulator, and start applying these ideas to your own Android apps! We look forward to seeing you on the Surface Duo.

Feedback

We would LOVE to hear from you about your experiences using the SDK, emulator, and your first thoughts on how you can use these in YOUR app.

Please reach out using the feedback forum or direct message me on Twitter .

And if you have not seen those already, we started a series of 1 minute videos to answer the most frequent questions

Author

Meir Ben Itay
Principal SW Engineer

Helping customers build enhanced dual-screen applications with foundational SDKs and guidance.

0 comments

Discussion are closed.