Large screen devices enable users to see more, do more, experience more. Large displays provide the opportunity to run multiple activities or multiple instances of the same activity simultaneously.
To take advantage of the extra display area of large screens, Jetpack WindowManager has introduced activity embedding, which splits an application’s task window among activities.

Updating legacy codebases to support large screens can be labor intensive and time consuming. Converting activity‑based apps to multi-pane layouts using fragments requires significant refactoring.
Activity embedding requires little or no refactoring of your app. You determine how your app displays its activities—side by side or stacked—by creating an XML configuration file or by making Jetpack WindowManager API calls.
Support for small screens is maintained automatically. When your app is on a device with a small screen, activities are stacked one on top of the other. On large screens, activities are displayed side by side. The system determines the presentation based on the configuration you’ve created—no branching logic required.
Activity embedding supports device orientation changes and works seamlessly on foldable devices, stacking and unstacking activities as the device folds and unfolds.
Modern android development uses a single-activity architecture with fragments, navigation components, and versatile layout managers like SlidingPaneLayout.
But if your app consists of multiple activities, activity embedding enables you to easily provide an enhanced user experience on tablets, foldables, and Chrome OS devices.
Split task window
Activity embedding splits the app task window into two containers: primary and secondary. The containers hold activities launched from the main activity or from other activities already in the containers.
Activities are stacked in the secondary container as they’re launched, and the secondary container is stacked on top of the primary container on small screens, so activity stacking and back navigation are consistent with the ordering of activities already built into your app.
Activity embedding enables you to display activities in a variety of ways. Your app can split the task window by launching two activities side by side simultaneously:

Or, an activity that’s occupying the entire task window can create a split by launching a new activity alongside:

Note: In this initial version of activity embedding, only the activity that created the split can occupy the primary container. The secondary container can contain a stack of activities.
Activities that are already in a split and sharing a task window can launch other activities in the following ways:
To the side on top of another activity:
Figure 4. Activity A starts activity C to the side over activity B. To the side, and shift the split sideways, concealing the previous primary activity:
Figure 5. Activity B starts activity C to the side and shifts the split sideways. Launch an activity in place on top; that is, in the same activity stack:
Figure 6. Activity B starts activity C with no extra intent flags. Launch an activity full window in the same task:
Figure 7. Activity A or activity B starts activity C which fills the task window.
Back navigation
Different types of applications can have different back navigation rules in a split task window state depending on the dependencies between activities or how users trigger the back event, for example:
- Going together: If activities are related, and one should not be shown without the other, back navigation can be configured to finish both.
- Going it alone: If activities are fully independent, back navigation on an activity does not affect the state of another activity in the task window.
The back event is sent to the last focused activity when using button navigation. With gesture based navigation, the back event is sent to the activity where the gesture occurred.
Multi-pane layout
Jetpack WindowManager 1.0 Beta03 enables you to build a multi-pane layout with
activities on large-screen devices with 12L (API level 32) and some
devices with earlier platform versions. Existing apps that are based on
multiple activities rather than fragments or view-based layouts such as
SlidingPaneLayout
can provide an improved large screen user experience without significant
refactoring.
One common example is a list-detail split. To ensure a quality presentation, the system starts the list activity, and then the application immediately starts the detail activity. The transition system waits until both activities are drawn, then displays them together. To the user, the two activities launch as one.

Split ratio
Your application can specify how the task window is proportioned by the
ratio
attribute of a split configuration (see
Split configuration below).

Placeholders
Placeholder activities are empty secondary activities that occupy an area of an activity split. They are ultimately meant to be replaced with another activity that contains content. For example, a placeholder activity could occupy the secondary side of an activity split in a list-detail layout until an item from the list is selected, at which point an activity containing the detail information for the selected list item replaces the placeholder.
Placeholders are shown only when there is enough space for a split. They are automatically finished when the display size changes to a width too small to display an activity split, but they are relaunched automatically (with a reinitialized state) when space permits.

Window size changes
When device configuration changes reduce the task window width so that it is not large enough for a multi-pane layout (for example, when a large screen foldable device folds from tablet size to phone size or the app window is resized in multi-window mode), the non-placeholder activities in the secondary pane of the task window are stacked on top of the activities in the primary pane.
Placeholder activities are shown only when there is enough display width for a split. On smaller screens, the placeholder is automatically dismissed. When the display area becomes large enough again, the placeholder is recreated. (See Placeholders above.)
Activity stacking is possible because
WindowManager
z-orders the
activities in the secondary pane above activities in the primary pane.
Multiple activities in secondary pane
Activity B starts activity C in place with no extra intent flags:
resulting in the following z-order of activities in the same task:
So, in a smaller task window, the application shrinks to a single activity with C at the top of the stack:
Navigating back in the smaller window navigates through the activities stacked on top of each other.
If the task window configuration is restored to a larger size that can accommodate multiple panes, the activities are displayed side by side again.
Stacked splits
Activity B starts activity C to the side and shifts the split sideways:
The result is the following z-order of activities in the same task:
In a smaller task window, the application shrinks to a single activity with C on top:
Split configuration
Containers and splits can be created by the WindowManager library based on split rules. Configuring split rules involves several steps:
Add the WindowManager library dependency to your build.gradle file:
implementation("androidx.window:window:1.0.0-beta03")
Create a resources file that does the following:
- Defines which activities should be split using filters
- Configures the split options for all activities that share a split
- Specifies activities that should never be put in a split
For example:
<!-- The split configuration for activities. --> <resources xmlns:window="http://schemas.android.com/apk/res-auto"> <!-- Automatically split the following activity pairs. --> <SplitPairRule window:splitRatio="0.3" window:splitMinWidth="600dp" window:finishPrimaryWithSecondary="true" window:finishSecondaryWithPrimary="true"> <SplitPairFilter window:primaryActivityName=".SplitActivityList" window:secondaryActivityName=".SplitActivityDetail"/> <SplitPairFilter window:primaryActivityName="*" window:secondaryActivityName="*/*" window:secondaryActivityAction="android.intent.action.VIEW"/> </SplitPairRule> <!-- Automatically launch a placeholder for the list activity. --> <SplitPlaceholderRule window:placeholderActivityName=".SplitActivityListPlaceholder" window:splitRatio="0.3" window:splitMinWidth="600dp"> <ActivityFilter window:activityName=".SplitActivityList"/> </SplitPlaceholderRule> </resources>
Inform the library about the rule definitions.
In this example we’re using the Jetpack Startup library to perform initialization before other components of the app load and activities start. To enable the startup functionality, add the library dependency in the app’s build file:
implementation("androidx.startup:startup-runtime:1.1.0")
and add the following entry in the app manifest:
<!-- AndroidManifest.xml --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <!-- This entry makes ExampleWindowInitializer discoverable. --> <meta-data android:name="androidx.window.sample.embedding.ExampleWindowInitializer" android:value="androidx.startup" /> </provider>
Finally, add the initializer class implementation.
The rules are set by providing the ID of the xml resource file that contains the definitions (main_split_config) to
SplitController.initialize()
:Kotlin
class ExampleWindowInitializer : Initializer<SplitController> { override fun create(context: Context): SplitController { SplitController.initialize(context, R.xml.main_split_config) return SplitController.getInstance(context) } override fun dependencies(): List<Class<out Initializer<*>>> { return emptyList() } }
Java
class ExampleWindowInitializer extends Initializer<SplitController> { @Override SplitController create(Context context) { SplitController.initialize(context, R.xml.main_split_config); return SplitController.getInstance(context); } @Override List<Class<? extends Initializer<?>>> dependencies() { return emptyList(); } }
Split examples
Split from full window

No refactoring required. You can define the configuration for the split
statically or at runtime and then call
Context#startActivity()
without any additional parameters.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Split by default
When the landing page of an application is designed to be split into two containers on large screens, the user experience is best when both activities are created and presented simultaneously. However, content might not be available for the secondary container of the split until the user interacts with the activity in the primary container (for example, the user selects an item from a navigation menu). A placeholder activity can fill the void until content can be displayed in the secondary container of the split (see Placeholders above).

To create a split with a placeholder, create a placeholder and associate it with the primary activity:
<SplitPlaceholderRule
window:placeholderIntentName=".Placeholder">
<ActivityFilter
window:activityName=".Main"/>
</SplitPlaceholderRule>
Deep link split
When an app receives an intent, the target activity can be shown as the secondary part of an activity split; for example, a request to show a detail screen with information about an item from a list. On small displays, the detail is shown in the full task window; on larger devices, beside the list.

The launch request should be routed to the main activity, and the target detail
activity should be launched in a split. SplitController
automatically chooses
the correct presentation—stacked or side by side—based on the available display
width.
Kotlin
override fun onCreate(savedInstanceState Bundle?) { … splitController.registerRule(SplitPairRule(newFilters)) startActivity(Intent(this, DetailActivity::class.java)) }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { … splitController.registerRule(new SplitPairRule(newFilters)); startActivity(new Intent(this, DetailActivity.class)); }
The deep link destination might be the only activity that should be available to the user in the back navigation stack, and you might want to avoid dismissing the detail activity and leaving only the main activity:
Instead, you can finish both activities at the same time by using the
finishPrimaryWithSecondary
attribute:
<SplitPairRule
window:finishPrimaryWithSecondary="true">
<SplitPairFilter
window:primaryActivityName=".List"
window:secondaryActivityName=".Detail"/>
</SplitPairRule>
Multiple activities in split containers
Stacking multiple activities in a split container enables users to access deep content. For example, with a list-detail split, the user might need to go into a sub-detail section but keep the primary activity in place:

Kotlin
class DetailActivity { … fun onOpenSubDetail() { startActivity(Intent(this, SubDetailActivity::class.java)) } }
Java
public class DetailActivity { … void onOpenSubDetail() { startActivity(new Intent(this, SubDetailActivity.class)); } }
The sub-detail activity is placed on top of the detail activity, concealing it:
The user can then go back to the previous detail level by navigating back through the stack:

Stacking activities on top of each other is the default behavior when activities are launched from an activity in the same secondary container. Activities launched from the primary container within an active split also end up in the secondary container on the top of the activity stack.
Activities in a new task
When activities in a split task window start activities in a new task, the new task is separate from the task that includes the split and is displayed full window. The Recents screen will show two tasks: the task in the split and the new task.

Activity replacement
Activities can be replaced in the secondary container stack; for example, when the primary activity is used for top-level navigation and the secondary activity is a selected destination. Each selection from the top-level navigation should start a new activity in the secondary container and remove the activity or activities that were previously there.

If the app doesn't finish the activity in the secondary container when the navigation selection changes, back navigation might be confusing when the split is collapsed (when the device is folded). For example, if you have a menu in the primary pane and screens A and B stacked in the secondary pane, when the user folds the phone, B is on top of A, and A is on top of the menu. When the user navigates back from B, A appears instead of the menu.
Screen A must be removed from the back stack in such cases.
The default behavior when launching to the side in a new container over an
existing split is to put the new secondary containers on top and retain the
old ones in the back stack. You can configure the splits to clear the previous
secondary containers with clearTop
and launch new activities normally.
<SplitPairRule
window:clearTop="true">
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenA"/>
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>
Kotlin
class MenuActivity { … fun onMenuItemSelected(selectedMenuItem: Int) { startActivity(Intent(this, classForItem(selectedMenuItem))) } }
Java
public class MenuActivity { … void onMenuItemSelected(int selectedMenuItem) { startActivity(new Intent(this, classForItem(selectedMenuItem))); } }
Alternatively, use the same secondary activity, and from the primary (menu) activity send new intents that resolve to the same instance but trigger a state or UI update in the secondary container.
Multiple splits
Apps can provide multi-level deep navigation by launching additional activities to the side.
When an activity in a secondary container launches a new activity to the side, a new split is created over top of the existing split.

The back stack contains all activities that were previously opened, so users can navigate to the A/B split after finishing C.
To create a new split, launch the new activity to the side from the existing secondary container. Declare the configurations for both the A/B and B/C splits and launch activity C normally from B:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
<SplitPairFilter
window:primaryActivityName=".B"
window:secondaryActivityName=".C"/>
</SplitPairRule>
Kotlin
class B { fun onOpenC() { startActivity(Intent(this, C::class.java)) } }
Java
public class B { … void onOpenC() { startActivity(new Intent(this, C.class)); } }
React to split state changes
Different activities in an app can have UI elements that perform the same function; for example, a control that opens a window containing account settings.

If two activities that have a UI element in common are in a split, it’s redundant and perhaps confusing to show the element in both activities.

To know when activities are in a split, register a listener with
SplitController
for changes in the split state. Then, adjust the UI
accordingly:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { splitController .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback()) } inner class SplitInfoChangeCallback : Consumer<List<SplitInfo>> { override fun accept(splitInfoList: List<SplitInfo>) { findViewById<View>(R.id.infoButton).visibility = if (!splitInfoList.isEmpty()) View.GONE else View.VISIBLE } }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { splitController .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback()); } class SplitInfoChangeCallback extends Consumer<List<SplitInfo>> { public void accept(List<SplitInfo> splitInfoList) { findViewById<View>(R.id.infoButton).visibility = !splitInfoList.isEmpty()) ? View.GONE : View.VISIBLE; } }
Callbacks can be made in any lifecycle state, including when an activity is
stopped. Listeners should usually be registered in onStart()
and
unregistered in onStop()
.
Full window modal
Some activities block users from interacting with the application until a specified action is performed; for example, a login screen activity, policy acknowledgement screen, or error message. Modal activities should be prevented from appearing in a split.
An activity can be forced to always fill the task window by using the expand configuration:
<ActivityRule
window:alwaysExpand="true">
<ActivityFilter
window:activityName=".FullWidthActivity"/>
</ActivityRule>
Finishing activities
Users can finish activities on either side of the split by swiping from the edge of the display:


If the device is set up to use the back button instead of gesture navigation, the input is sent to the focused activity—the activity that was touched or launched last.
The result of finishing one of the activities in the split depends on the split configuration.
Default configuration
When one activity in the split is finished, the remaining activity occupies the entire window:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Finish activities together
Finish the primary activity automatically when the secondary activity is finished:
<SplitPairRule
window:finishPrimaryWithSecondary="true">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Finish the secondary activity automatically when the primary activity is finished:
<SplitPairRule
window:finishSecondaryWithPrimary="true">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Finish activities together when either the primary or secondary is finished:
<SplitPairRule
window:finishPrimaryWithSecondary="true"
window:finishSecondaryWithPrimary="true">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Finish multiple activities in containers
If multiple activities are stacked in a split container, finishing an activity on the bottom of the stack does not automatically finish activities on top.
For example, if two activities are in the secondary container, C on top of B:
and the configuration of the split is defined by the configuration of activities A and B:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
finishing the top activity retains the split.
Finishing the bottom (root) activity of the secondary container does not remove the activities on top of it; and so, also retains the split.
Any additional rules for finishing activities together, such as finishing the secondary activity with the primary, are also executed:
<SplitPairRule
window:finishSecondaryWithPrimary="true">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
And when the split is configured to finish primary and secondary together:
<SplitPairRule
window:finishPrimaryWithSecondary="true"
window:finishSecondaryWithPrimary="true">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Change split properties at runtime
The properties of a currently active and visible split cannot be changed. Changing the split rules affects additional activity launches and new containers, but not existing and active splits.
To change the properties of active splits, finish the side activity or activities in the split and launch to the side again with a new configuration.
Extract an activity from a split to full window
Create a new configuration that will display the side activity full window, and then relaunch the activity with an intent that resolves to the same instance.
Check for split support at runtime
Activity embedding is an 12L (API level 32) feature, but it will be
available on some devices with earlier platform versions. To check at runtime
for the availability of the feature, use the
SplitController.isSplitSupported()
method:
Kotlin
val splitController = SplitController.Companion.getInstance() if (splitController.isSplitSupported()) { // Device supports split activity features. }
Java
SplitController splitController = SplitController.Companion.getInstance(); if (splitController.isSplitSupported()) { // Device supports split activity features. }
If splits are not supported, activities are launched on top (following the regular model).
Limitations, restrictions, and caveats
- Only the host app of the task, which is identified as the owner of the root activity in the task, can organize and embed other activities in the task. If activities that support embedding and splits run in a task that belongs to a different application, then embedding and splits will not work for those activities.
- Activities can only be organized within a single task. Launching an activity in a new task always puts it in a new expanded window outside of any existing splits.
- Only activities in the same process can be organized and put in a split. The
SplitInfo
callback only reports activities that belong to the same process, since there is no way of knowing about activities in different processes. - Each pair or singular activity rule applies only to activity launches that happen after the rule has been registered. There is currently no way to update existing splits or their visual properties.
- The split pair filter configuration must match the intents used when launching activities completely. The matching occurs at the point when a new activity is started from the application process, so it might not know about component names that are resolved later in the system process when using implicit intents. If a component name is not known at the time of launch, a wildcard can be used instead (“*/*”) and filtering can be performed based on intent action.
- There is currently no way to move activities between containers or in and out of splits after they were created. Splits are only created by the WindowManager library when new activities with matching rules are launched, and splits are destroyed when the last activity in a split container is finished.
- Activities can be relaunched when the configuration changes, so when a split is created or removed and activity bounds change, the activity can go through complete destruction of the previous instance and creation of the new one. As a result, app developers should be careful with things like launching new activities from lifecycle callbacks.