Merge "No embed activity when Task is in PIP" into tm-dev
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package androidx.window.extensions.embedding;
|
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.getFinishPrimaryWithSecondaryBehavior;
|
||||||
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
|
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
|
||||||
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
|
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
|
||||||
@@ -93,7 +95,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
mSplitRules.clear();
|
mSplitRules.clear();
|
||||||
mSplitRules.addAll(rules);
|
mSplitRules.addAll(rules);
|
||||||
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final boolean wasInPip = isInPictureInPicture(container);
|
||||||
container.setInfo(taskFragmentInfo);
|
container.setInfo(taskFragmentInfo);
|
||||||
|
final boolean isInPip = isInPictureInPicture(container);
|
||||||
// Check if there are no running activities - consider the container empty if there are no
|
// Check if there are no running activities - consider the container empty if there are no
|
||||||
// non-finishing activities left.
|
// non-finishing activities left.
|
||||||
if (!taskFragmentInfo.hasRunningActivity()) {
|
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
|
// Do not finish the dependents if this TaskFragment was cleared due to launching
|
||||||
// activity in the Task.
|
// activity in the Task.
|
||||||
final boolean shouldFinishDependent =
|
final boolean shouldFinishDependent =
|
||||||
!taskFragmentInfo.isTaskClearedForReuse();
|
!taskFragmentInfo.isTaskClearedForReuse();
|
||||||
mPresenter.cleanupContainer(container, shouldFinishDependent);
|
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();
|
updateCallbackIfNecessary();
|
||||||
}
|
}
|
||||||
@@ -174,10 +192,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
@Override
|
@Override
|
||||||
public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
|
public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
|
||||||
@NonNull Configuration parentConfig) {
|
@NonNull Configuration parentConfig) {
|
||||||
TaskFragmentContainer container = getContainer(fragmentToken);
|
final TaskFragmentContainer container = getContainer(fragmentToken);
|
||||||
if (container != null) {
|
if (container != null) {
|
||||||
onTaskBoundsMayChange(container.getTaskId(),
|
onTaskConfigurationChanged(container.getTaskId(), parentConfig);
|
||||||
parentConfig.windowConfiguration.getBounds());
|
if (isInPictureInPicture(parentConfig)) {
|
||||||
|
// No need to update presentation in PIP until the Task exit PIP.
|
||||||
|
return;
|
||||||
|
}
|
||||||
mPresenter.updateContainer(container);
|
mPresenter.updateContainer(container);
|
||||||
updateCallbackIfNecessary();
|
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);
|
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
||||||
if (taskContainer != null && !taskBounds.isEmpty()
|
if (taskContainer == null) {
|
||||||
&& !taskContainer.mTaskBounds.equals(taskBounds)) {
|
return;
|
||||||
taskContainer.mTaskBounds.set(taskBounds);
|
|
||||||
updateAnimationOverride(taskId);
|
|
||||||
}
|
}
|
||||||
|
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
|
* 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.
|
* bounds is large enough for at least one split rule.
|
||||||
*/
|
*/
|
||||||
private void updateAnimationOverride(int taskId) {
|
private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
|
||||||
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
if (!taskContainer.isTaskBoundsInitialized()) {
|
||||||
if (taskContainer == null || !taskContainer.isTaskBoundsInitialized()) {
|
|
||||||
// We don't know about the Task bounds yet.
|
// We don't know about the Task bounds yet.
|
||||||
return;
|
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.
|
// Check if the parent container bounds can support any split rule.
|
||||||
boolean supportSplit = false;
|
|
||||||
for (EmbeddingRule rule : mSplitRules) {
|
for (EmbeddingRule rule : mSplitRules) {
|
||||||
if (!(rule instanceof SplitRule)) {
|
if (!(rule instanceof SplitRule)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
|
if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
|
||||||
supportSplit = true;
|
return true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// We only want to override if it supports split.
|
|
||||||
if (supportSplit) {
|
|
||||||
mPresenter.startOverrideSplitAnimation(taskId);
|
|
||||||
} else {
|
|
||||||
mPresenter.stopOverrideSplitAnimation(taskId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onActivityCreated(@NonNull Activity launchedActivity) {
|
void onActivityCreated(@NonNull Activity launchedActivity) {
|
||||||
@@ -250,6 +297,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
*/
|
*/
|
||||||
// TODO(b/190433398): Break down into smaller functions.
|
// TODO(b/190433398): Break down into smaller functions.
|
||||||
void handleActivityCreated(@NonNull Activity launchedActivity) {
|
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 List<EmbeddingRule> splitRules = getSplitRules();
|
||||||
final TaskFragmentContainer currentContainer = getContainerWithActivity(
|
final TaskFragmentContainer currentContainer = getContainerWithActivity(
|
||||||
launchedActivity.getActivityToken());
|
launchedActivity.getActivityToken());
|
||||||
@@ -324,6 +375,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onActivityConfigurationChanged(@NonNull Activity activity) {
|
private void onActivityConfigurationChanged(@NonNull Activity activity) {
|
||||||
|
if (isInPictureInPicture(activity)) {
|
||||||
|
// We don't embed activity when it is in PIP.
|
||||||
|
return;
|
||||||
|
}
|
||||||
final TaskFragmentContainer currentContainer = getContainerWithActivity(
|
final TaskFragmentContainer currentContainer = getContainerWithActivity(
|
||||||
activity.getActivityToken());
|
activity.getActivityToken());
|
||||||
|
|
||||||
@@ -365,9 +420,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
}
|
}
|
||||||
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
final TaskContainer taskContainer = mTaskContainers.get(taskId);
|
||||||
taskContainer.mContainers.add(container);
|
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.
|
// Initial check before any TaskFragment has appeared.
|
||||||
onTaskBoundsMayChange(taskId, SplitPresenter.getTaskBoundsFromActivity(activity));
|
updateAnimationOverride(taskContainer);
|
||||||
}
|
}
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
@@ -389,6 +446,40 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
mTaskContainers.get(primaryContainer.getTaskId()).mSplitContainers.add(splitContainer);
|
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.
|
* Removes the container from bookkeeping records.
|
||||||
*/
|
*/
|
||||||
@@ -916,6 +1007,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
return super.onStartActivity(who, intent, options);
|
return super.onStartActivity(who, intent, options);
|
||||||
}
|
}
|
||||||
final Activity launchingActivity = (Activity) who;
|
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())) {
|
if (shouldExpand(null, intent, getSplitRules())) {
|
||||||
setLaunchingInExpandedContainer(launchingActivity, options);
|
setLaunchingInExpandedContainer(launchingActivity, options);
|
||||||
@@ -1079,6 +1174,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
return !pairRule.shouldClearTop();
|
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. */
|
/** Represents TaskFragments and split pairs below a Task. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static class TaskContainer {
|
static class TaskContainer {
|
||||||
@@ -1095,6 +1203,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
final Set<IBinder> mFinishedContainer = new ArraySet<>();
|
final Set<IBinder> mFinishedContainer = new ArraySet<>();
|
||||||
/** Available window bounds of this Task. */
|
/** Available window bounds of this Task. */
|
||||||
final Rect mTaskBounds = new Rect();
|
final Rect mTaskBounds = new Rect();
|
||||||
|
/** Configuration of the Task. */
|
||||||
|
@Nullable
|
||||||
|
Configuration mConfiguration;
|
||||||
|
|
||||||
TaskContainer(int taskId) {
|
TaskContainer(int taskId) {
|
||||||
mTaskId = taskId;
|
mTaskId = taskId;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import androidx.annotation.IntDef;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,12 +66,26 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
|||||||
/**
|
/**
|
||||||
* Updates the presentation of the provided container.
|
* Updates the presentation of the provided container.
|
||||||
*/
|
*/
|
||||||
void updateContainer(TaskFragmentContainer container) {
|
void updateContainer(@NonNull TaskFragmentContainer container) {
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
mController.updateContainer(wct, container);
|
mController.updateContainer(wct, container);
|
||||||
applyTransaction(wct);
|
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
|
* Deletes the specified container and all other associated and dependent containers in the same
|
||||||
* transaction.
|
* transaction.
|
||||||
|
|||||||
@@ -191,6 +191,13 @@ class TaskFragmentContainer {
|
|||||||
mContainersToFinishOnExit.add(containerToFinish);
|
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.
|
* Adds an activity that should be finished when this container is finished.
|
||||||
*/
|
*/
|
||||||
@@ -198,6 +205,19 @@ class TaskFragmentContainer {
|
|||||||
mActivitiesToFinishOnExit.add(activityToFinish);
|
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
|
* Removes all activities that belong to this process and finishes other containers/activities
|
||||||
* configured to finish together.
|
* configured to finish together.
|
||||||
|
|||||||
Reference in New Issue
Block a user