34,333
edits
Changes
Created page with "Android 7 introduced a new feature in the form of multi-window support. Unlike previous versions of Android, multi-window support in Android 7 allowed more than one activity t..."
Android 7 introduced a new feature in the form of multi-window support. Unlike previous versions of Android, multi-window support in Android 7 allowed more than one activity to be displayed on the device screen at one time. In this chapter, an overview of Android multi-window modes will be provided from both user and app developer perspectives.
Once the basics of multi-window support have been covered, the next chapter will work through a tutorial outlining the practical steps involved in working with multi-window mode when developing Android apps.
== Split-Screen, Freeform and Picture-in-Picture Modes ==
Multi-window support in Android provides three different forms of window support. Split-screen mode, available on most phone and tablet devices, provides a split screen environment where two activities appear either side by side or one above the other. A moveable divider is provided which, when dragged by the user, adjusts the percentage of the screen assigned to each of the adjacent activities:

[[File:]]
Freeform mode provides a windowing environment on devices with larger screens and is currently enabled at the discretion of the device manufacturer. Freeform differs from split-screen mode in that it allows each activity to appear in a separate, resizable window and is not limited to two activities being displayed concurrently. Figure 60-2, for example, shows a device in freeform mode with the Calculator and Contacts apps displayed in separate windows:

[[File:]]
Picture-in-picture support, as the name suggests, allows video playback to continue in a smaller window while the user performs other tasks. At present this feature is only available on Android TV and, as such, is outside the scope of this book.
== Entering Multi-Window Mode ==
Split-screen mode can be entered by pressing and holding the square Overview button until the display switches mode. Once in split-screen mode, the Overview button will change to display two rectangles as shown in Figure 60-3 and the current activity will fill one half of the screen. The Overview screen will appear in the adjacent half of the screen allowing the second activity to be selected for display:

[[File:]]
Alternatively, an app may be placed in split-screen mode by displaying the Overview screen, pressing and holding the title bar of a listed app and then dragging and dropping the app onto the highlighted section of the screen.
To exit split-screen mode, simply drag the divider separating the two activities to a far edge so that only one activity fills the screen, or press and hold the Overview button until it reverts to a single square.
In the case of freeform mode, an additional button appears within the title bar of the apps when listed in the Overview screen. When selected, this button (highlighted in Figure 60-4) causes the activity to appear in a freeform window:

[[File:]]
The additional button located in the title bar of a freeform activity (shown in Figure 60-5) may be pressed to return the activity to full screen mode:

[[File:]]
== Enabling Freeform Support ==
Although not officially supported on all devices, it is possible to enable freeform multi-window mode on large screen devices and emulators. To enable this mode, run the following adb command while the emulator is running, or the device is connected:
<pre>
adb shell settings put global enable_freeform_support 1
</pre>
After making this change, it may be necessary to reboot the device before the setting takes effect.
== Checking for Freeform Support ==
As outlined earlier in the chapter, Google is leaving the choice of whether to enable freeform multi-window mode to the individual Android device manufacturers. Since it only makes sense to use freeform on larger devices, there is no guarantee that freeform will be available on every device on which an app is likely to run. Fortunately all of the freeform specific methods and attributes are ignored by the system if freeform mode is not available on a device, so using these will not cause the app to crash on a non-freeform device. Situations might arise, however, where it may be useful to be able to detect if a device supports freeform multi-window mode. Fortunately, this can be achieved by checking for the freeform window management feature in the package manager. The following code example checks for freeform multi-window support and returns a Boolean value based on the result of the test:
<pre>
fun checkFreeform(): Boolean {
return packageManager.hasSystemFeature(
PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
}
</pre>
== Enabling Multi-Window Support in an App ==
The android:resizableActivity manifest file setting controls whether multi-window behavior is supported by an app. This setting can be made at either the application or individual activity levels. The following fragment, for example, configures the activity named MainActivity to support both split-screen and freeform multi-window modes:
<pre>
<activity
android:name=".MainActivity"
android:resizeableActivity="true"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</pre>
Setting the property to false will prevent the activity from appearing in split-screen or freeform mode. Launching an activity for which multi-window support is disabled will result in a message appearing indicating that the app does not support multi-window mode and the activity filling the entire screen. When a device is in multi-window mode, the title bar of such activities will also display a message within the Overview screen indicating that multi-window mode is not supported by the activity (Figure 60-6):

[[File:]]
== Specifying Multi-Window Attributes ==
A number of attributes are available as part of the <layout> element for specifying the size and placement of an activity when it is launched into a multi-window mode. The initial height, width and position of an activity when launched in freeform mode may be specified using the following attributes:
* '''android:defaultWidth''' – Specifies the default width of the activity.
* '''android:defaultHeight''' – Specifies the default height of the activity.
* '''android:gravity''' – Specifies the initial position of the activity (start, end, left, right, top etc.).
Note that the above attributes apply to the activity only when it is displayed in freeform mode. The following example configures an activity to appear with a specific height and width at the top of the starting edge of the screen:
<pre>
<activity android:name=".MainActivity ">
<layout android:defaultHeight="350dp"
android:defaultWidth="450dp"
android:gravity="start|end" />
</activity>
</pre>
The following <layout> attributes may be used to specify the minimum width and height to which an activity may be reduced in either split-view or freeform modes:
android:minimalHeight – Specifies the minimum height to which the activity may be reduced while in split-screen or freeform mode.
android:minimalWidth - Specifies the minimum width to which the activity may be reduced while in split-screen or freeform mode.
When the user slides the split-screen divider beyond the minimal height or width boundaries, the system will stop resizing the layout of the shrinking activity and simply clip the user interface to make room for the adjacent activity.
The following manifest file fragment implements the minimal width and height attributes for an activity:
<pre>
<activity android:name=".MainActivity ">
<layout android:minimalHeight="400dp"
android:minimalWidth="290dp" />
</activity>
</pre>
== Detecting Multi-Window Mode in an Activity ==
Situations may arise where an activity needs to detect whether it is currently being displayed to the user in multi-window mode. The current status can be obtained via a call to the isInMultiWindowMode() method of the Activity class. When called, this method returns a true or false value depending on whether or not the activity is currently full screen:
<pre>
if (this.isInMultiWindowMode()) {
// Activity is running in Multi-Window mode
} else {
// Activity is not in Multi-Window mode
}
</pre>
== Receiving Multi-Window Notifications ==
An activity will receive notification that it is entering or exiting multi-window mode if it overrides the onMultiWindowModeChanged() callback method. The first argument passed to this method is true on entering multi-window mode, and false when the activity exits the mode. The new configuration settings are contained within the Configuration object passed as the second argument:
<pre>
override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean,
newConfig: Configuration?) {
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig)
if (isInMultiWindowMode) {
// Activity has entered multi-window mode
} else {
// Activity has exited multi-window mode
}
}
</pre>
== Launching an Activity in Multi-Window Mode ==
In the “Android Explicit Intents – A Worked Example” chapter of this book, an example app was created in which an activity uses an intent to launch a second activity. By default, activities launched via an intent are considered to reside in the same task stack as the originating activity. An activity can, however, be launched into a new task stack by passing through the appropriate flags with the intent.
When an activity in multi-window mode launches another activity within the same task stack, the new activity replaces the originating activity within the split-screen or freeform window (the user returns to the original activity via the back button).
When launched into a new task stack in split-screen mode, however, the second activity will appear in the window adjacent to the original activity, allowing both activities to be viewed simultaneously. In the case of freeform mode, the launched activity will appear in a separate window from the original activity.
In order to launch an activity into a new task stack, the following flags must be set on the intent before it is started:
* Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
* Intent.FLAG_ACTIVITY_MULTIPLE_TASK
* Intent.FLAG_ACTIVITY_NEW_TASK
The following code, for example, configures and launches a second activity designed to appear in a separate window:
<pre>
val i = Intent(this, SecondActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(i)
</pre>
== Configuring Freeform Activity Size and Position ==
By default, an activity launched into a different task stack while in freeform mode will be positioned in the center of the screen at a size dictated by the system. The location and dimensions of this window can be controlled by passing launch bounds settings to the intent via the ActivityOptions class. The first step in this process is to create a Rect object configured with the left (X), top (Y), right (X) and bottom (Y) coordinates of the rectangle representing the activity window. The following code, for example, creates a Rect object in which the top-left corner is positioned at coordinate (100, 800) and the bottom-right at (900, 700):
<pre>
val rect = Rect(0, 0, 100, 100)
</pre>
The next step is to create a basic instance of the ActivityOptions class and initialize it with the Rect settings via the setLaunchBounds() method:
<pre>
val options = ActivityOptions.makeBasic()
val bounds = options.setLaunchBounds(rect)
</pre>
Finally, the ActivityOptions instance is converted to a Bundle object and passed to the startActivity() method along with the Intent object:
<pre>
startActivity(i, bounds.toBundle())
</pre>
Combining these steps results in a code sequence that reads as follows:
<pre>
val i = Intent(this, SecondActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK)
val rect = Rect(0, 0, 100, 100)
val options = ActivityOptions.makeBasic()
val bounds = options.setLaunchBounds(rect)
startActivity(i, bounds.toBundle())
</pre>
When the second activity is launched by the intent while the originating activity is in freeform mode, the new activity window will appear with the location and dimensions defined in the Rect object.
== Summary ==
Android 7 introduced multi-window support, a system whereby more than one activity is displayed on the screen at any one time. The three modes provided by multi-window support are split-screen, freeform and picture-in-picture. In split-screen mode, the screen is split either horizontally or vertically into two panes with an activity displayed in each pane. Freeform mode, which is only supported on certain Android devices, allows each activity to appear in a separate, movable and resizable window. Picture-in-picture mode is only available on Android TV and allows video playback to continue in a small window while the user is performing other tasks.
As outlined in this chapter, a number of methods and property settings are available within the Android SDK to detect, respond to and control multi-window behavior within an app.
Once the basics of multi-window support have been covered, the next chapter will work through a tutorial outlining the practical steps involved in working with multi-window mode when developing Android apps.
== Split-Screen, Freeform and Picture-in-Picture Modes ==
Multi-window support in Android provides three different forms of window support. Split-screen mode, available on most phone and tablet devices, provides a split screen environment where two activities appear either side by side or one above the other. A moveable divider is provided which, when dragged by the user, adjusts the percentage of the screen assigned to each of the adjacent activities:

[[File:]]
Freeform mode provides a windowing environment on devices with larger screens and is currently enabled at the discretion of the device manufacturer. Freeform differs from split-screen mode in that it allows each activity to appear in a separate, resizable window and is not limited to two activities being displayed concurrently. Figure 60-2, for example, shows a device in freeform mode with the Calculator and Contacts apps displayed in separate windows:

[[File:]]
Picture-in-picture support, as the name suggests, allows video playback to continue in a smaller window while the user performs other tasks. At present this feature is only available on Android TV and, as such, is outside the scope of this book.
== Entering Multi-Window Mode ==
Split-screen mode can be entered by pressing and holding the square Overview button until the display switches mode. Once in split-screen mode, the Overview button will change to display two rectangles as shown in Figure 60-3 and the current activity will fill one half of the screen. The Overview screen will appear in the adjacent half of the screen allowing the second activity to be selected for display:

[[File:]]
Alternatively, an app may be placed in split-screen mode by displaying the Overview screen, pressing and holding the title bar of a listed app and then dragging and dropping the app onto the highlighted section of the screen.
To exit split-screen mode, simply drag the divider separating the two activities to a far edge so that only one activity fills the screen, or press and hold the Overview button until it reverts to a single square.
In the case of freeform mode, an additional button appears within the title bar of the apps when listed in the Overview screen. When selected, this button (highlighted in Figure 60-4) causes the activity to appear in a freeform window:

[[File:]]
The additional button located in the title bar of a freeform activity (shown in Figure 60-5) may be pressed to return the activity to full screen mode:

[[File:]]
== Enabling Freeform Support ==
Although not officially supported on all devices, it is possible to enable freeform multi-window mode on large screen devices and emulators. To enable this mode, run the following adb command while the emulator is running, or the device is connected:
<pre>
adb shell settings put global enable_freeform_support 1
</pre>
After making this change, it may be necessary to reboot the device before the setting takes effect.
== Checking for Freeform Support ==
As outlined earlier in the chapter, Google is leaving the choice of whether to enable freeform multi-window mode to the individual Android device manufacturers. Since it only makes sense to use freeform on larger devices, there is no guarantee that freeform will be available on every device on which an app is likely to run. Fortunately all of the freeform specific methods and attributes are ignored by the system if freeform mode is not available on a device, so using these will not cause the app to crash on a non-freeform device. Situations might arise, however, where it may be useful to be able to detect if a device supports freeform multi-window mode. Fortunately, this can be achieved by checking for the freeform window management feature in the package manager. The following code example checks for freeform multi-window support and returns a Boolean value based on the result of the test:
<pre>
fun checkFreeform(): Boolean {
return packageManager.hasSystemFeature(
PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
}
</pre>
== Enabling Multi-Window Support in an App ==
The android:resizableActivity manifest file setting controls whether multi-window behavior is supported by an app. This setting can be made at either the application or individual activity levels. The following fragment, for example, configures the activity named MainActivity to support both split-screen and freeform multi-window modes:
<pre>
<activity
android:name=".MainActivity"
android:resizeableActivity="true"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</pre>
Setting the property to false will prevent the activity from appearing in split-screen or freeform mode. Launching an activity for which multi-window support is disabled will result in a message appearing indicating that the app does not support multi-window mode and the activity filling the entire screen. When a device is in multi-window mode, the title bar of such activities will also display a message within the Overview screen indicating that multi-window mode is not supported by the activity (Figure 60-6):

[[File:]]
== Specifying Multi-Window Attributes ==
A number of attributes are available as part of the <layout> element for specifying the size and placement of an activity when it is launched into a multi-window mode. The initial height, width and position of an activity when launched in freeform mode may be specified using the following attributes:
* '''android:defaultWidth''' – Specifies the default width of the activity.
* '''android:defaultHeight''' – Specifies the default height of the activity.
* '''android:gravity''' – Specifies the initial position of the activity (start, end, left, right, top etc.).
Note that the above attributes apply to the activity only when it is displayed in freeform mode. The following example configures an activity to appear with a specific height and width at the top of the starting edge of the screen:
<pre>
<activity android:name=".MainActivity ">
<layout android:defaultHeight="350dp"
android:defaultWidth="450dp"
android:gravity="start|end" />
</activity>
</pre>
The following <layout> attributes may be used to specify the minimum width and height to which an activity may be reduced in either split-view or freeform modes:
android:minimalHeight – Specifies the minimum height to which the activity may be reduced while in split-screen or freeform mode.
android:minimalWidth - Specifies the minimum width to which the activity may be reduced while in split-screen or freeform mode.
When the user slides the split-screen divider beyond the minimal height or width boundaries, the system will stop resizing the layout of the shrinking activity and simply clip the user interface to make room for the adjacent activity.
The following manifest file fragment implements the minimal width and height attributes for an activity:
<pre>
<activity android:name=".MainActivity ">
<layout android:minimalHeight="400dp"
android:minimalWidth="290dp" />
</activity>
</pre>
== Detecting Multi-Window Mode in an Activity ==
Situations may arise where an activity needs to detect whether it is currently being displayed to the user in multi-window mode. The current status can be obtained via a call to the isInMultiWindowMode() method of the Activity class. When called, this method returns a true or false value depending on whether or not the activity is currently full screen:
<pre>
if (this.isInMultiWindowMode()) {
// Activity is running in Multi-Window mode
} else {
// Activity is not in Multi-Window mode
}
</pre>
== Receiving Multi-Window Notifications ==
An activity will receive notification that it is entering or exiting multi-window mode if it overrides the onMultiWindowModeChanged() callback method. The first argument passed to this method is true on entering multi-window mode, and false when the activity exits the mode. The new configuration settings are contained within the Configuration object passed as the second argument:
<pre>
override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean,
newConfig: Configuration?) {
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig)
if (isInMultiWindowMode) {
// Activity has entered multi-window mode
} else {
// Activity has exited multi-window mode
}
}
</pre>
== Launching an Activity in Multi-Window Mode ==
In the “Android Explicit Intents – A Worked Example” chapter of this book, an example app was created in which an activity uses an intent to launch a second activity. By default, activities launched via an intent are considered to reside in the same task stack as the originating activity. An activity can, however, be launched into a new task stack by passing through the appropriate flags with the intent.
When an activity in multi-window mode launches another activity within the same task stack, the new activity replaces the originating activity within the split-screen or freeform window (the user returns to the original activity via the back button).
When launched into a new task stack in split-screen mode, however, the second activity will appear in the window adjacent to the original activity, allowing both activities to be viewed simultaneously. In the case of freeform mode, the launched activity will appear in a separate window from the original activity.
In order to launch an activity into a new task stack, the following flags must be set on the intent before it is started:
* Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
* Intent.FLAG_ACTIVITY_MULTIPLE_TASK
* Intent.FLAG_ACTIVITY_NEW_TASK
The following code, for example, configures and launches a second activity designed to appear in a separate window:
<pre>
val i = Intent(this, SecondActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(i)
</pre>
== Configuring Freeform Activity Size and Position ==
By default, an activity launched into a different task stack while in freeform mode will be positioned in the center of the screen at a size dictated by the system. The location and dimensions of this window can be controlled by passing launch bounds settings to the intent via the ActivityOptions class. The first step in this process is to create a Rect object configured with the left (X), top (Y), right (X) and bottom (Y) coordinates of the rectangle representing the activity window. The following code, for example, creates a Rect object in which the top-left corner is positioned at coordinate (100, 800) and the bottom-right at (900, 700):
<pre>
val rect = Rect(0, 0, 100, 100)
</pre>
The next step is to create a basic instance of the ActivityOptions class and initialize it with the Rect settings via the setLaunchBounds() method:
<pre>
val options = ActivityOptions.makeBasic()
val bounds = options.setLaunchBounds(rect)
</pre>
Finally, the ActivityOptions instance is converted to a Bundle object and passed to the startActivity() method along with the Intent object:
<pre>
startActivity(i, bounds.toBundle())
</pre>
Combining these steps results in a code sequence that reads as follows:
<pre>
val i = Intent(this, SecondActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK)
val rect = Rect(0, 0, 100, 100)
val options = ActivityOptions.makeBasic()
val bounds = options.setLaunchBounds(rect)
startActivity(i, bounds.toBundle())
</pre>
When the second activity is launched by the intent while the originating activity is in freeform mode, the new activity window will appear with the location and dimensions defined in the Rect object.
== Summary ==
Android 7 introduced multi-window support, a system whereby more than one activity is displayed on the screen at any one time. The three modes provided by multi-window support are split-screen, freeform and picture-in-picture. In split-screen mode, the screen is split either horizontally or vertically into two panes with an activity displayed in each pane. Freeform mode, which is only supported on certain Android devices, allows each activity to appear in a separate, movable and resizable window. Picture-in-picture mode is only available on Android TV and allows video playback to continue in a small window while the user is performing other tasks.
As outlined in this chapter, a number of methods and property settings are available within the Android SDK to detect, respond to and control multi-window behavior within an app.