Add support for sticky placeholders

Normally placeholders are finished when there is not enough space
to show them by the side of the primary activity. However, in some
cases it makes sense to continue showing the placeholder stacked
on top when the window is small after it first appeared in a split
with a larger window size.

This CL introduces an option to make placeholders sticky and behave
similar to regular split rules in stacked case for consistency.

Bug: 202810486
Test: New option in the demo app
Change-Id: I60e8973679d95f52aaa231581a3063d9f30d4be3
This commit is contained in:
Andrii Kulian
2021-11-01 18:32:34 -07:00
parent db38b7faf4
commit b777d3581c
4 changed files with 60 additions and 0 deletions

View File

@@ -84,6 +84,13 @@ class SplitContainer {
return shouldFinishSecondaryWithPrimary || isPlaceholderContainer; return shouldFinishSecondaryWithPrimary || isPlaceholderContainer;
} }
static boolean isStickyPlaceholderRule(@NonNull SplitRule splitRule) {
if (!(splitRule instanceof SplitPlaceholderRule)) {
return false;
}
return ((SplitPlaceholderRule) splitRule).isSticky();
}
@Override @Override
public String toString() { public String toString() {
return "SplitContainer{" return "SplitContainer{"

View File

@@ -16,6 +16,8 @@
package androidx.window.extensions.embedding; package androidx.window.extensions.embedding;
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.Activity; import android.app.Activity;
@@ -460,6 +462,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return false; return false;
} }
if (isStickyPlaceholderRule(splitContainer.getSplitRule())) {
// The placeholder should remain after it was first shown.
return false;
}
if (mPresenter.shouldShowSideBySide(splitContainer)) { if (mPresenter.shouldShowSideBySide(splitContainer)) {
return false; return false;
} }
@@ -643,6 +650,46 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return false; return false;
} }
/**
* Checks whether the associated container should be destroyed together with a finishing
* container. There is a case when primary containers for placeholders should be retained
* despite the rule configuration to finish primary with secondary - if they are marked as
* 'sticky' and the placeholder was finished when fully overlapping the primary container.
* @return {@code true} if the associated container should be retained (and not be finished).
*/
boolean shouldRetainAssociatedContainer(@NonNull TaskFragmentContainer finishingContainer,
@NonNull TaskFragmentContainer associatedContainer) {
SplitContainer splitContainer = getActiveSplitForContainers(associatedContainer,
finishingContainer);
if (splitContainer == null) {
// Containers are not in the same split, no need to retain.
return false;
}
if (!isStickyPlaceholderRule(splitContainer.getSplitRule())) {
// Currently only the containers associated with sticky placeholders can be retained.
return false;
}
// When sticky placeholder is stacked on top of the main container it should not cause the
// destruction of the primary one.
return !mPresenter.shouldShowSideBySide(splitContainer);
}
/**
* @see #shouldRetainAssociatedContainer(TaskFragmentContainer, TaskFragmentContainer)
*/
boolean shouldRetainAssociatedActivity(@NonNull TaskFragmentContainer finishingContainer,
@NonNull Activity associatedActivity) {
TaskFragmentContainer associatedContainer = getContainerWithActivity(
associatedActivity.getActivityToken());
if (associatedContainer == null) {
return false;
}
return shouldRetainAssociatedContainer(finishingContainer, associatedContainer);
}
private final class LifecycleCallbacks implements ActivityLifecycleCallbacks { private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
@Override @Override

View File

@@ -228,6 +228,9 @@ class TaskFragmentContainer {
// Finish dependent containers // Finish dependent containers
for (TaskFragmentContainer container : mContainersToFinishOnExit) { for (TaskFragmentContainer container : mContainersToFinishOnExit) {
if (controller.shouldRetainAssociatedContainer(this, container)) {
continue;
}
container.finish(true /* shouldFinishDependent */, presenter, container.finish(true /* shouldFinishDependent */, presenter,
wct, controller); wct, controller);
} }
@@ -235,6 +238,9 @@ class TaskFragmentContainer {
// Finish associated activities // Finish associated activities
for (Activity activity : mActivitiesToFinishOnExit) { for (Activity activity : mActivitiesToFinishOnExit) {
if (controller.shouldRetainAssociatedActivity(this, activity)) {
continue;
}
activity.finish(); activity.finish();
} }
mActivitiesToFinishOnExit.clear(); mActivitiesToFinishOnExit.clear();