Merge "No embed activity when Task is in PIP" into tm-dev am: a6b968f887
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17665556 Change-Id: I23b50a3b749c8d6fd4e6395df5b3a35f0c32522b Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
package androidx.window.extensions.embedding;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
|
||||
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
|
||||
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
|
||||
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
|
||||
@@ -93,7 +95,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
mSplitRules.clear();
|
||||
mSplitRules.addAll(rules);
|
||||
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
|
||||
updateAnimationOverride(mTaskContainers.keyAt(i));
|
||||
updateAnimationOverride(mTaskContainers.valueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,15 +149,31 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean wasInPip = isInPictureInPicture(container);
|
||||
container.setInfo(taskFragmentInfo);
|
||||
final boolean isInPip = isInPictureInPicture(container);
|
||||
// Check if there are no running activities - consider the container empty if there are no
|
||||
// non-finishing activities left.
|
||||
if (!taskFragmentInfo.hasRunningActivity()) {
|
||||
// TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP
|
||||
// Task.
|
||||
// Do not finish the dependents if this TaskFragment was cleared due to launching
|
||||
// activity in the Task.
|
||||
final boolean shouldFinishDependent =
|
||||
!taskFragmentInfo.isTaskClearedForReuse();
|
||||
mPresenter.cleanupContainer(container, shouldFinishDependent);
|
||||
} else if (wasInPip && isInPip) {
|
||||
// No update until exit PIP.
|
||||
return;
|
||||
} else if (isInPip) {
|
||||
// Enter PIP.
|
||||
// All overrides will be cleanup.
|
||||
container.setLastRequestedBounds(null /* bounds */);
|
||||
cleanupForEnterPip(container);
|
||||
} else if (wasInPip) {
|
||||
// Exit PIP.
|
||||
// Updates the presentation of the container. Expand or launch placeholder if needed.
|
||||
mPresenter.updateContainer(container);
|
||||
}
|
||||
updateCallbackIfNecessary();
|
||||
}
|
||||
@@ -174,10 +192,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
@Override
|
||||
public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
|
||||
@NonNull Configuration parentConfig) {
|
||||
TaskFragmentContainer container = getContainer(fragmentToken);
|
||||
final TaskFragmentContainer container = getContainer(fragmentToken);
|
||||
if (container != null) {
|
||||
onTaskBoundsMayChange(container.getTaskId(),
|
||||
parentConfig.windowConfiguration.getBounds());
|
||||
onTaskConfigurationChanged(container.getTaskId(), parentConfig);
|
||||
if (isInPictureInPicture(parentConfig)) {
|
||||
// No need to update presentation in PIP until the Task exit PIP.
|
||||
return;
|
||||
}
|
||||
mPresenter.updateContainer(container);
|
||||
updateCallbackIfNecessary();
|
||||
}
|
||||
@@ -199,44 +220,70 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
}
|
||||
}
|
||||
|
||||
private void onTaskBoundsMayChange(int taskId, @NonNull Rect taskBounds) {
|
||||
private void onTaskConfigurationChanged(int taskId, @NonNull Configuration config) {
|
||||
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
||||
if (taskContainer != null && !taskBounds.isEmpty()
|
||||
&& !taskContainer.mTaskBounds.equals(taskBounds)) {
|
||||
taskContainer.mTaskBounds.set(taskBounds);
|
||||
updateAnimationOverride(taskId);
|
||||
if (taskContainer == null) {
|
||||
return;
|
||||
}
|
||||
final boolean wasInPip = isInPictureInPicture(taskContainer.mConfiguration);
|
||||
final boolean isInPIp = isInPictureInPicture(config);
|
||||
taskContainer.mConfiguration = config;
|
||||
|
||||
// We need to check the animation override when enter/exit PIP or has bounds changed.
|
||||
boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
|
||||
if (onTaskBoundsMayChange(taskContainer, config.windowConfiguration.getBounds())
|
||||
&& !isInPIp) {
|
||||
// We don't care the bounds change when it has already entered PIP.
|
||||
shouldUpdateAnimationOverride = true;
|
||||
}
|
||||
if (shouldUpdateAnimationOverride) {
|
||||
updateAnimationOverride(taskContainer);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns {@code true} if the bounds is changed. */
|
||||
private boolean onTaskBoundsMayChange(@NonNull TaskContainer taskContainer,
|
||||
@NonNull Rect taskBounds) {
|
||||
if (!taskBounds.isEmpty() && !taskContainer.mTaskBounds.equals(taskBounds)) {
|
||||
taskContainer.mTaskBounds.set(taskBounds);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates if we should override transition animation. We only want to override if the Task
|
||||
* bounds is large enough for at least one split rule.
|
||||
*/
|
||||
private void updateAnimationOverride(int taskId) {
|
||||
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
||||
if (taskContainer == null || !taskContainer.isTaskBoundsInitialized()) {
|
||||
private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
|
||||
if (!taskContainer.isTaskBoundsInitialized()) {
|
||||
// We don't know about the Task bounds yet.
|
||||
return;
|
||||
}
|
||||
|
||||
// We only want to override if it supports split.
|
||||
if (supportSplit(taskContainer)) {
|
||||
mPresenter.startOverrideSplitAnimation(taskContainer.mTaskId);
|
||||
} else {
|
||||
mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean supportSplit(@NonNull TaskContainer taskContainer) {
|
||||
// No split inside PIP.
|
||||
if (isInPictureInPicture(taskContainer.mConfiguration)) {
|
||||
return false;
|
||||
}
|
||||
// Check if the parent container bounds can support any split rule.
|
||||
boolean supportSplit = false;
|
||||
for (EmbeddingRule rule : mSplitRules) {
|
||||
if (!(rule instanceof SplitRule)) {
|
||||
continue;
|
||||
}
|
||||
if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
|
||||
supportSplit = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We only want to override if it supports split.
|
||||
if (supportSplit) {
|
||||
mPresenter.startOverrideSplitAnimation(taskId);
|
||||
} else {
|
||||
mPresenter.stopOverrideSplitAnimation(taskId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void onActivityCreated(@NonNull Activity launchedActivity) {
|
||||
@@ -250,6 +297,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
*/
|
||||
// TODO(b/190433398): Break down into smaller functions.
|
||||
void handleActivityCreated(@NonNull Activity launchedActivity) {
|
||||
if (isInPictureInPicture(launchedActivity)) {
|
||||
// We don't embed activity when it is in PIP.
|
||||
return;
|
||||
}
|
||||
final List<EmbeddingRule> splitRules = getSplitRules();
|
||||
final TaskFragmentContainer currentContainer = getContainerWithActivity(
|
||||
launchedActivity.getActivityToken());
|
||||
@@ -324,6 +375,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
}
|
||||
|
||||
private void onActivityConfigurationChanged(@NonNull Activity activity) {
|
||||
if (isInPictureInPicture(activity)) {
|
||||
// We don't embed activity when it is in PIP.
|
||||
return;
|
||||
}
|
||||
final TaskFragmentContainer currentContainer = getContainerWithActivity(
|
||||
activity.getActivityToken());
|
||||
|
||||
@@ -365,9 +420,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
}
|
||||
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
||||
taskContainer.mContainers.add(container);
|
||||
if (activity != null && !taskContainer.isTaskBoundsInitialized()) {
|
||||
if (activity != null && !taskContainer.isTaskBoundsInitialized()
|
||||
&& onTaskBoundsMayChange(taskContainer,
|
||||
SplitPresenter.getTaskBoundsFromActivity(activity))) {
|
||||
// Initial check before any TaskFragment has appeared.
|
||||
onTaskBoundsMayChange(taskId, SplitPresenter.getTaskBoundsFromActivity(activity));
|
||||
updateAnimationOverride(taskContainer);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
@@ -389,6 +446,40 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
mTaskContainers.get(primaryContainer.getTaskId()).mSplitContainers.add(splitContainer);
|
||||
}
|
||||
|
||||
/** Cleanups all the dependencies when the TaskFragment is entering PIP. */
|
||||
private void cleanupForEnterPip(@NonNull TaskFragmentContainer container) {
|
||||
final int taskId = container.getTaskId();
|
||||
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
||||
if (taskContainer == null) {
|
||||
return;
|
||||
}
|
||||
final List<SplitContainer> splitsToRemove = new ArrayList<>();
|
||||
final Set<TaskFragmentContainer> containersToUpdate = new ArraySet<>();
|
||||
for (SplitContainer splitContainer : taskContainer.mSplitContainers) {
|
||||
if (splitContainer.getPrimaryContainer() != container
|
||||
&& splitContainer.getSecondaryContainer() != container) {
|
||||
continue;
|
||||
}
|
||||
splitsToRemove.add(splitContainer);
|
||||
final TaskFragmentContainer splitTf = splitContainer.getPrimaryContainer() == container
|
||||
? splitContainer.getSecondaryContainer()
|
||||
: splitContainer.getPrimaryContainer();
|
||||
containersToUpdate.add(splitTf);
|
||||
// We don't want the PIP TaskFragment to be removed as a result of any of its dependents
|
||||
// being removed.
|
||||
splitTf.removeContainerToFinishOnExit(container);
|
||||
if (container.getTopNonFinishingActivity() != null) {
|
||||
splitTf.removeActivityToFinishOnExit(container.getTopNonFinishingActivity());
|
||||
}
|
||||
}
|
||||
container.resetDependencies();
|
||||
taskContainer.mSplitContainers.removeAll(splitsToRemove);
|
||||
// If there is any TaskFragment split with the PIP TaskFragment, update their presentations
|
||||
// since the split is dismissed.
|
||||
// We don't want to close any of them even if they are dependencies of the PIP TaskFragment.
|
||||
mPresenter.updateContainers(containersToUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the container from bookkeeping records.
|
||||
*/
|
||||
@@ -916,6 +1007,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
return super.onStartActivity(who, intent, options);
|
||||
}
|
||||
final Activity launchingActivity = (Activity) who;
|
||||
if (isInPictureInPicture(launchingActivity)) {
|
||||
// We don't embed activity when it is in PIP.
|
||||
return super.onStartActivity(who, intent, options);
|
||||
}
|
||||
|
||||
if (shouldExpand(null, intent, getSplitRules())) {
|
||||
setLaunchingInExpandedContainer(launchingActivity, options);
|
||||
@@ -1079,6 +1174,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
return !pairRule.shouldClearTop();
|
||||
}
|
||||
|
||||
private static boolean isInPictureInPicture(@NonNull Activity activity) {
|
||||
return isInPictureInPicture(activity.getResources().getConfiguration());
|
||||
}
|
||||
|
||||
private static boolean isInPictureInPicture(@NonNull TaskFragmentContainer tf) {
|
||||
return isInPictureInPicture(tf.getInfo().getConfiguration());
|
||||
}
|
||||
|
||||
private static boolean isInPictureInPicture(@Nullable Configuration configuration) {
|
||||
return configuration != null
|
||||
&& configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
|
||||
}
|
||||
|
||||
/** Represents TaskFragments and split pairs below a Task. */
|
||||
@VisibleForTesting
|
||||
static class TaskContainer {
|
||||
@@ -1095,6 +1203,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
final Set<IBinder> mFinishedContainer = new ArraySet<>();
|
||||
/** Available window bounds of this Task. */
|
||||
final Rect mTaskBounds = new Rect();
|
||||
/** Configuration of the Task. */
|
||||
@Nullable
|
||||
Configuration mConfiguration;
|
||||
|
||||
TaskContainer(int taskId) {
|
||||
mTaskId = taskId;
|
||||
|
||||
@@ -36,6 +36,7 @@ import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@@ -65,12 +66,26 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
/**
|
||||
* Updates the presentation of the provided container.
|
||||
*/
|
||||
void updateContainer(TaskFragmentContainer container) {
|
||||
void updateContainer(@NonNull TaskFragmentContainer container) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
mController.updateContainer(wct, container);
|
||||
applyTransaction(wct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the presentation of the provided containers.
|
||||
*/
|
||||
void updateContainers(@NonNull Collection<TaskFragmentContainer> containers) {
|
||||
if (containers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
for (TaskFragmentContainer container : containers) {
|
||||
mController.updateContainer(wct, container);
|
||||
}
|
||||
applyTransaction(wct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified container and all other associated and dependent containers in the same
|
||||
* transaction.
|
||||
|
||||
@@ -191,6 +191,13 @@ class TaskFragmentContainer {
|
||||
mContainersToFinishOnExit.add(containerToFinish);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a container that should be finished when this container is finished.
|
||||
*/
|
||||
void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) {
|
||||
mContainersToFinishOnExit.remove(containerToRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an activity that should be finished when this container is finished.
|
||||
*/
|
||||
@@ -198,6 +205,19 @@ class TaskFragmentContainer {
|
||||
mActivitiesToFinishOnExit.add(activityToFinish);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an activity that should be finished when this container is finished.
|
||||
*/
|
||||
void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) {
|
||||
mActivitiesToFinishOnExit.remove(activityToRemove);
|
||||
}
|
||||
|
||||
/** Removes all dependencies that should be finished when this container is finished. */
|
||||
void resetDependencies() {
|
||||
mContainersToFinishOnExit.clear();
|
||||
mActivitiesToFinishOnExit.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all activities that belong to this process and finishes other containers/activities
|
||||
* configured to finish together.
|
||||
|
||||
Reference in New Issue
Block a user