Implement the runtime APIs

Implement the following APIs:
- finishActivityStacks
Finish a set of ActivityStacks.
It is useful to expand the primary container.

- invalidateTopVisibleSplitAttributes:
Update the container actively and trigger the SplitAttrbutes
calculatator function.
It is useful to update SplitAttributes regardless of
device and window state change.

- updateSplitAttributes
Update the SplitAttributes of a specific split and also
overrides its default SplitAttributes.
It is useful if an app doesn't set its SplitAttributes
calculator function, but want to customize SplitAttributes of
a split pair without updating the rule.

Bug: 263565444
Test: presubmit

Change-Id: Icc502f5e106d380ed1e5656445fbd20a1a391c8e
This commit is contained in:
Charles Chen
2023-02-01 17:38:06 +08:00
committed by Chris Li
parent bef72a409e
commit eca505edab
8 changed files with 286 additions and 120 deletions

View File

@@ -21,9 +21,11 @@ import android.os.Binder;
import android.os.IBinder;
import android.util.Pair;
import android.util.Size;
import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.window.extensions.core.util.function.Function;
/**
* Client-side descriptor of a split that holds two containers.
@@ -35,8 +37,12 @@ class SplitContainer {
private final TaskFragmentContainer mSecondaryContainer;
@NonNull
private final SplitRule mSplitRule;
/** @see SplitContainer#getCurrentSplitAttributes() */
@NonNull
private SplitAttributes mSplitAttributes;
private SplitAttributes mCurrentSplitAttributes;
/** @see SplitContainer#getDefaultSplitAttributes() */
@NonNull
private SplitAttributes mDefaultSplitAttributes;
@NonNull
private final IBinder mToken;
@@ -48,7 +54,8 @@ class SplitContainer {
mPrimaryContainer = primaryContainer;
mSecondaryContainer = secondaryContainer;
mSplitRule = splitRule;
mSplitAttributes = splitAttributes;
mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
mCurrentSplitAttributes = splitAttributes;
mToken = new Binder("SplitContainer");
if (shouldFinishPrimaryWithSecondary(splitRule)) {
@@ -82,9 +89,37 @@ class SplitContainer {
return mSplitRule;
}
/**
* Returns the current {@link SplitAttributes} this {@code SplitContainer} is showing.
* <p>
* If the {@code SplitAttributes} calculator function is not set by
* {@link SplitController#setSplitAttributesCalculator(Function)}, the current
* {@code SplitAttributes} is either to expand the containers if the size constraints of
* {@link #getSplitRule()} are not satisfied,
* or the {@link #getDefaultSplitAttributes()}, otherwise.
* </p><p>
* If the {@code SplitAttributes} calculator function is set, the current
* {@code SplitAttributes} will be customized by the function, which can be any
* {@code SplitAttributes}.
* </p>
*
* @see SplitAttributes.SplitType.ExpandContainersSplitType
*/
@NonNull
SplitAttributes getSplitAttributes() {
return mSplitAttributes;
SplitAttributes getCurrentSplitAttributes() {
return mCurrentSplitAttributes;
}
/**
* Returns the default {@link SplitAttributes} when the parent task container bounds satisfy
* {@link #getSplitRule()} constraints.
* <p>
* The value is usually from {@link SplitRule#getDefaultSplitAttributes} unless it is overridden
* by {@link SplitController#updateSplitAttributes(IBinder, SplitAttributes)}.
*/
@NonNull
SplitAttributes getDefaultSplitAttributes() {
return mDefaultSplitAttributes;
}
@NonNull
@@ -95,11 +130,19 @@ class SplitContainer {
/**
* Updates the {@link SplitAttributes} to this container.
* It is usually used when there's a folding state change or
* {@link SplitController#onTaskFragmentParentInfoChanged(WindowContainerTransaction, int,
* Configuration)}.
* {@link SplitController#onTaskFragmentParentInfoChanged(WindowContainerTransaction,
* int, TaskFragmentParentInfo)}.
*/
void setSplitAttributes(@NonNull SplitAttributes splitAttributes) {
mSplitAttributes = splitAttributes;
void updateCurrentSplitAttributes(@NonNull SplitAttributes splitAttributes) {
mCurrentSplitAttributes = splitAttributes;
}
/**
* Overrides the default {@link SplitAttributes} to this container, which may be different
* from {@link SplitRule#getDefaultSplitAttributes}.
*/
void updateDefaultSplitAttributes(@NonNull SplitAttributes splitAttributes) {
mDefaultSplitAttributes = splitAttributes;
}
@NonNull
@@ -121,7 +164,7 @@ class SplitContainer {
@NonNull
SplitInfo toSplitInfo() {
return new SplitInfo(mPrimaryContainer.toActivityStack(),
mSecondaryContainer.toActivityStack(), mSplitAttributes, mToken);
mSecondaryContainer.toActivityStack(), mCurrentSplitAttributes, mToken);
}
static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
@@ -180,9 +223,10 @@ class SplitContainer {
public String toString() {
return "SplitContainer{"
+ " primaryContainer=" + mPrimaryContainer
+ " secondaryContainer=" + mSecondaryContainer
+ " splitRule=" + mSplitRule
+ " splitAttributes" + mSplitAttributes
+ ", secondaryContainer=" + mSecondaryContainer
+ ", splitRule=" + mSplitRule
+ ", currentSplitAttributes" + mCurrentSplitAttributes
+ ", defaultSplitAttributes" + mDefaultSplitAttributes
+ "}";
}
}

View File

@@ -87,6 +87,7 @@ import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -279,6 +280,98 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
@Override
public void finishActivityStacks(@NonNull Set<IBinder> activityStackTokens) {
if (activityStackTokens.isEmpty()) {
return;
}
synchronized (mLock) {
// Translate ActivityStack to TaskFragmentContainer.
final List<TaskFragmentContainer> pendingFinishingContainers =
activityStackTokens.stream()
.map(token -> {
synchronized (mLock) {
return getContainer(token);
}
}).filter(Objects::nonNull)
.toList();
if (pendingFinishingContainers.isEmpty()) {
return;
}
// Start transaction with close transit type.
final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
transactionRecord.setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
final WindowContainerTransaction wct = transactionRecord.getTransaction();
forAllTaskContainers(taskContainer -> {
synchronized (mLock) {
final List<TaskFragmentContainer> containers = taskContainer.mContainers;
// Clean up the TaskFragmentContainers by the z-order from the lowest.
for (int i = 0; i < containers.size() - 1; i++) {
final TaskFragmentContainer container = containers.get(i);
if (pendingFinishingContainers.contains(container)) {
// Don't update records here to prevent double invocation.
container.finish(false /* shouldFinishDependant */, mPresenter,
wct, this, false /* shouldRemoveRecord */);
}
}
// Remove container records.
removeContainers(taskContainer, pendingFinishingContainers);
// Update the change to the client side.
updateContainersInTaskIfVisible(wct, taskContainer.getTaskId());
}
});
// Apply the transaction.
transactionRecord.apply(false /* shouldApplyIndependently */);
}
}
@Override
public void invalidateTopVisibleSplitAttributes() {
synchronized (mLock) {
WindowContainerTransaction wct = mTransactionManager.startNewTransaction()
.getTransaction();
forAllTaskContainers(taskContainer -> {
synchronized (mLock) {
updateContainersInTaskIfVisible(wct, taskContainer.getTaskId());
}
});
mTransactionManager.getCurrentTransactionRecord()
.apply(false /* shouldApplyIndependently */);
}
}
@GuardedBy("mLock")
private void forAllTaskContainers(@NonNull Consumer<TaskContainer> callback) {
for (int i = mTaskContainers.size() - 1; i >= 0; --i) {
callback.accept(mTaskContainers.valueAt(i));
}
}
@Override
public void updateSplitAttributes(@NonNull IBinder splitInfoToken,
@NonNull SplitAttributes splitAttributes) {
synchronized (mLock) {
final SplitContainer splitContainer = getSplitContainer(splitInfoToken);
if (splitContainer == null) {
Log.w(TAG, "Cannot find SplitContainer for token:" + splitInfoToken);
return;
}
WindowContainerTransaction wct = mTransactionManager.startNewTransaction()
.getTransaction();
if (updateSplitContainerIfNeeded(splitContainer, wct, splitAttributes)) {
splitContainer.updateDefaultSplitAttributes(splitAttributes);
mTransactionManager.getCurrentTransactionRecord()
.apply(false /* shouldApplyIndependently */);
} else {
// Abort if the SplitContainer wasn't updated.
mTransactionManager.getCurrentTransactionRecord().abort();
}
}
}
/**
* Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction.
@@ -648,35 +741,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
/** Returns whether the given {@link TaskContainer} may show in split. */
// Suppress GuardedBy warning because lint asks to mark this method as
// @GuardedBy(mPresenter.mController.mLock), which is mLock itself
@SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
private boolean mayShowSplit(@NonNull TaskContainer taskContainer) {
// No split inside PIP.
if (taskContainer.isInPictureInPicture()) {
return false;
}
// Always assume the TaskContainer if SplitAttributesCalculator is set
if (mSplitAttributesCalculator != null) {
return true;
}
// Check if the parent container bounds can support any split rule.
for (EmbeddingRule rule : mSplitRules) {
if (!(rule instanceof SplitRule)) {
continue;
}
final SplitRule splitRule = (SplitRule) rule;
final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(
taskContainer.getTaskProperties(), splitRule, null /* minDimensionsPair */);
if (shouldShowSplit(splitAttributes)) {
return true;
}
}
return false;
}
@VisibleForTesting
@GuardedBy("mLock")
void onActivityCreated(@NonNull WindowContainerTransaction wct,
@@ -1360,20 +1424,33 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Removes the container from bookkeeping records.
*/
void removeContainer(@NonNull TaskFragmentContainer container) {
removeContainers(container.getTaskContainer(), Collections.singletonList(container));
}
/**
* Removes containers from bookkeeping records.
*/
void removeContainers(@NonNull TaskContainer taskContainer,
@NonNull List<TaskFragmentContainer> containers) {
// Remove all split containers that included this one
final TaskContainer taskContainer = container.getTaskContainer();
taskContainer.mContainers.remove(container);
taskContainer.mContainers.removeAll(containers);
// Marked as a pending removal which will be removed after it is actually removed on the
// server side (#onTaskFragmentVanished).
// In this way, we can keep track of the Task bounds until we no longer have any
// TaskFragment there.
taskContainer.mFinishedContainer.add(container.getTaskFragmentToken());
taskContainer.mFinishedContainer.addAll(containers.stream().map(
TaskFragmentContainer::getTaskFragmentToken).toList());
// Cleanup any split references.
final List<SplitContainer> containersToRemove = new ArrayList<>();
for (SplitContainer splitContainer : taskContainer.mSplitContainers) {
if (container.equals(splitContainer.getSecondaryContainer())
|| container.equals(splitContainer.getPrimaryContainer())) {
if (containersToRemove.contains(splitContainer)) {
// Don't need to check because it has been in the remove list.
continue;
}
if (containers.stream().anyMatch(container ->
splitContainer.getPrimaryContainer().equals(container)
|| splitContainer.getSecondaryContainer().equals(container))) {
containersToRemove.add(splitContainer);
}
}
@@ -1381,7 +1458,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Cleanup any dependent references.
for (TaskFragmentContainer containerToUpdate : taskContainer.mContainers) {
containerToUpdate.removeContainerToFinishOnExit(container);
containerToUpdate.removeContainersToFinishOnExit(containers);
}
}
@@ -1461,26 +1538,53 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (splitContainer == null) {
return;
}
updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */);
}
/**
* Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
* {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
* are {@code null}, the {@link SplitAttributes} will be calculated with
* {@link SplitPresenter#computeSplitAttributes(TaskContainer.TaskProperties, SplitRule, Pair)}.
*
* @param splitContainer The {@link SplitContainer} to update
* @param splitAttributes Update with this {@code splitAttributes} if it is not {@code null}.
* Otherwise, use the value calculated by
* {@link SplitPresenter#computeSplitAttributes(
* TaskContainer.TaskProperties, SplitRule, Pair)}
*
* @return {@code true} if the update succeed. Otherwise, returns {@code false}.
*/
@GuardedBy("mLock")
private boolean updateSplitContainerIfNeeded(@NonNull SplitContainer splitContainer,
@NonNull WindowContainerTransaction wct, @Nullable SplitAttributes splitAttributes) {
if (!isTopMostSplit(splitContainer)) {
// Skip position update - it isn't the topmost split.
return;
return false;
}
if (splitContainer.getPrimaryContainer().isFinished()
|| splitContainer.getSecondaryContainer().isFinished()) {
// Skip position update - one or both containers are finished.
return;
return false;
}
final TaskContainer taskContainer = splitContainer.getTaskContainer();
final SplitRule splitRule = splitContainer.getSplitRule();
final Pair<Size, Size> minDimensionsPair = splitContainer.getMinDimensionsPair();
final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(
taskContainer.getTaskProperties(), splitRule, minDimensionsPair);
splitContainer.setSplitAttributes(splitAttributes);
if (splitAttributes == null) {
final TaskContainer.TaskProperties taskProperties = splitContainer.getTaskContainer()
.getTaskProperties();
final SplitRule splitRule = splitContainer.getSplitRule();
final SplitAttributes defaultSplitAttributes = splitContainer
.getDefaultSplitAttributes();
final Pair<Size, Size> minDimensionsPair = splitContainer.getMinDimensionsPair();
splitAttributes = mPresenter.computeSplitAttributes(taskProperties, splitRule,
defaultSplitAttributes, minDimensionsPair);
}
splitContainer.updateCurrentSplitAttributes(splitAttributes);
if (dismissPlaceholderIfNecessary(wct, splitContainer)) {
// Placeholder was finished, the positions will be updated when its container is emptied
return;
return true;
}
mPresenter.updateSplitContainer(splitContainer, container, wct);
mPresenter.updateSplitContainer(splitContainer, wct);
return true;
}
/** Whether the given split is the topmost split in the Task. */
@@ -1576,7 +1680,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(activity,
placeholderRule.getPlaceholderIntent());
final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(taskProperties,
placeholderRule, minDimensionsPair);
placeholderRule, placeholderRule.getDefaultSplitAttributes(), minDimensionsPair);
if (!SplitPresenter.shouldShowSplit(splitAttributes)) {
return false;
}
@@ -1655,7 +1759,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// The placeholder should remain after it was first shown.
return false;
}
final SplitAttributes splitAttributes = splitContainer.getSplitAttributes();
final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes();
if (SplitPresenter.shouldShowSplit(splitAttributes)) {
return false;
}
@@ -1797,6 +1901,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null;
}
@Nullable
@GuardedBy("mLock")
SplitContainer getSplitContainer(@NonNull IBinder token) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<SplitContainer> containers = mTaskContainers.valueAt(i).mSplitContainers;
for (SplitContainer container : containers) {
if (container.getToken().equals(token)) {
return container;
}
}
}
return null;
}
@Nullable
@GuardedBy("mLock")
TaskContainer getTaskContainer(int taskId) {

View File

@@ -179,7 +179,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(
primaryActivity, secondaryIntent);
final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
minDimensionsPair);
rule.getDefaultSplitAttributes(), minDimensionsPair);
final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
@@ -225,7 +225,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final Pair<Size, Size> minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity,
secondaryActivity);
final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
minDimensionsPair);
rule.getDefaultSplitAttributes(), minDimensionsPair);
final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
@@ -334,11 +334,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
/**
* Updates the positions of containers in an existing split.
* @param splitContainer The split container to be updated.
* @param updatedContainer The task fragment that was updated and caused this split update.
* @param wct WindowContainerTransaction that this update should be performed with.
*/
void updateSplitContainer(@NonNull SplitContainer splitContainer,
@NonNull TaskFragmentContainer updatedContainer,
@NonNull WindowContainerTransaction wct) {
// Getting the parent configuration using the updated container - it will have the recent
// value.
@@ -348,8 +346,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
if (activity == null) {
return;
}
final TaskProperties taskProperties = getTaskProperties(updatedContainer);
final SplitAttributes splitAttributes = splitContainer.getSplitAttributes();
final TaskContainer taskContainer = splitContainer.getTaskContainer();
final TaskProperties taskProperties = taskContainer.getTaskProperties();
final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes();
final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
@@ -370,7 +369,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// When placeholder is shown in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
}
final TaskContainer taskContainer = updatedContainer.getTaskContainer();
final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
primaryRelBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
@@ -515,9 +513,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// Expand the splitContainer if minimum dimensions are not satisfied.
final TaskContainer taskContainer = splitContainer.getTaskContainer();
final SplitAttributes splitAttributes = sanitizeSplitAttributes(
taskContainer.getTaskProperties(), splitContainer.getSplitAttributes(),
taskContainer.getTaskProperties(), splitContainer.getCurrentSplitAttributes(),
minDimensionsPair);
splitContainer.setSplitAttributes(splitAttributes);
splitContainer.updateCurrentSplitAttributes(splitAttributes);
if (!shouldShowSplit(splitAttributes)) {
// If the client side hasn't received TaskFragmentInfo yet, we can't change TaskFragment
// bounds. Return failure to create a new SplitContainer which fills task bounds.
@@ -540,7 +538,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
}
static boolean shouldShowSplit(@NonNull SplitContainer splitContainer) {
return shouldShowSplit(splitContainer.getSplitAttributes());
return shouldShowSplit(splitContainer.getCurrentSplitAttributes());
}
static boolean shouldShowSplit(@NonNull SplitAttributes splitAttributes) {
@@ -549,12 +547,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull
SplitAttributes computeSplitAttributes(@NonNull TaskProperties taskProperties,
@NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
@NonNull SplitRule rule, @NonNull SplitAttributes defaultSplitAttributes,
@Nullable Pair<Size, Size> minDimensionsPair) {
final Configuration taskConfiguration = taskProperties.getConfiguration();
final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
mController.getSplitAttributesCalculator();
final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes();
final boolean areDefaultConstraintsSatisfied = rule.checkParentMetrics(taskWindowMetrics);
if (calculator == null) {
if (!areDefaultConstraintsSatisfied) {
@@ -956,11 +954,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
return bounds.width() > bounds.height();
}
@NonNull
static TaskProperties getTaskProperties(@NonNull TaskFragmentContainer container) {
return container.getTaskContainer().getTaskProperties();
}
@NonNull
TaskProperties getTaskProperties(@NonNull Activity activity) {
final TaskContainer taskContainer = mController.getTaskContainer(

View File

@@ -107,12 +107,6 @@ class TaskContainer {
return mIsVisible;
}
@NonNull
Configuration getConfiguration() {
// Make a copy in case the config is updated unexpectedly.
return new Configuration(mConfiguration);
}
@NonNull
TaskProperties getTaskProperties() {
return new TaskProperties(mDisplayId, mConfiguration);
@@ -157,7 +151,7 @@ class TaskContainer {
@WindowingMode
private int getWindowingMode() {
return getConfiguration().windowConfiguration.getWindowingMode();
return mConfiguration.windowConfiguration.getWindowingMode();
}
/** Whether there is any {@link TaskFragmentContainer} below this Task. */
@@ -220,10 +214,7 @@ class TaskContainer {
}
}
/**
* A wrapper class which contains the display ID and {@link Configuration} of a
* {@link TaskContainer}
*/
/** A wrapper class which contains the information of {@link TaskContainer} */
static final class TaskProperties {
private final int mDisplayId;
@NonNull

View File

@@ -37,6 +37,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -436,10 +437,17 @@ class TaskFragmentContainer {
* Removes a container that should be finished when this container is finished.
*/
void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) {
removeContainersToFinishOnExit(Collections.singletonList(containerToRemove));
}
/**
* Removes container list that should be finished when this container is finished.
*/
void removeContainersToFinishOnExit(@NonNull List<TaskFragmentContainer> containersToRemove) {
if (mIsFinished) {
return;
}
mContainersToFinishOnExit.remove(containerToRemove);
mContainersToFinishOnExit.removeAll(containersToRemove);
}
/**
@@ -478,6 +486,16 @@ class TaskFragmentContainer {
@GuardedBy("mController.mLock")
void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
finish(shouldFinishDependent, presenter, wct, controller, true /* shouldRemoveRecord */);
}
/**
* Removes all activities that belong to this process and finishes other containers/activities
* configured to finish together.
*/
void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller,
boolean shouldRemoveRecord) {
if (!mIsFinished) {
mIsFinished = true;
if (mAppearEmptyTimeout != null) {
@@ -494,8 +512,10 @@ class TaskFragmentContainer {
// Cleanup the visuals
presenter.deleteTaskFragment(wct, getTaskFragmentToken());
// Cleanup the records
controller.removeContainer(this);
if (shouldRemoveRecord) {
// Cleanup the records
controller.removeContainer(this);
}
// Clean up task fragment information
mInfo = null;
}

View File

@@ -275,7 +275,8 @@ public class SplitControllerTest {
assertNotNull(tf);
assertNotNull(taskContainer);
assertEquals(TASK_BOUNDS, taskContainer.getConfiguration().windowConfiguration.getBounds());
assertEquals(TASK_BOUNDS, taskContainer.getTaskProperties().getConfiguration()
.windowConfiguration.getBounds());
}
@Test
@@ -288,7 +289,7 @@ public class SplitControllerTest {
doReturn(true).when(tf).isEmpty();
doReturn(true).when(mSplitController).launchPlaceholderIfNecessary(mTransaction,
mActivity, false /* isOnCreated */);
doNothing().when(mSplitPresenter).updateSplitContainer(any(), any(), any());
doNothing().when(mSplitPresenter).updateSplitContainer(any(), any());
mSplitController.updateContainer(mTransaction, tf);
@@ -341,7 +342,7 @@ public class SplitControllerTest {
mSplitController.updateContainer(mTransaction, tf);
verify(mSplitPresenter, never()).updateSplitContainer(any(), any(), any());
verify(mSplitPresenter, never()).updateSplitContainer(any(), any());
// Verify if the top active split is updated if both of its containers are not finished.
doReturn(false).when(mSplitController)
@@ -349,7 +350,7 @@ public class SplitControllerTest {
mSplitController.updateContainer(mTransaction, tf);
verify(mSplitPresenter).updateSplitContainer(splitContainer, tf, mTransaction);
verify(mSplitPresenter).updateSplitContainer(splitContainer, mTransaction);
}
@Test
@@ -366,14 +367,14 @@ public class SplitControllerTest {
doReturn(false).when(taskContainer).isVisible();
mSplitController.updateContainer(mTransaction, taskFragmentContainer);
verify(mSplitPresenter, never()).updateSplitContainer(any(), any(), any());
verify(mSplitPresenter, never()).updateSplitContainer(any(), any());
// Update the split when the Task is visible.
doReturn(true).when(taskContainer).isVisible();
mSplitController.updateContainer(mTransaction, taskFragmentContainer);
verify(mSplitPresenter).updateSplitContainer(taskContainer.mSplitContainers.get(0),
taskFragmentContainer, mTransaction);
mTransaction);
}
@Test

View File

@@ -228,7 +228,7 @@ public class SplitPresenterTest {
@Test
public void testGetRelBoundsForPosition_expandContainers() {
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final TaskContainer.TaskProperties taskProperties = getTaskProperties();
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType())
.build();
@@ -248,7 +248,7 @@ public class SplitPresenterTest {
@Test
public void testGetRelBoundsForPosition_expandContainers_isRelativeToParent() {
final TaskContainer.TaskProperties taskProperties = getTaskProperty(
final TaskContainer.TaskProperties taskProperties = getTaskProperties(
new Rect(100, 100, 500, 1000));
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType())
@@ -273,7 +273,7 @@ public class SplitPresenterTest {
false /* splitHorizontally */);
final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
false /* splitHorizontally */);
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final TaskContainer.TaskProperties taskProperties = getTaskProperties();
SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
@@ -339,7 +339,7 @@ public class SplitPresenterTest {
// Offset TaskBounds to 100, 100. The returned rel bounds shouldn't be affected.
final Rect taskBounds = new Rect(TASK_BOUNDS);
taskBounds.offset(100, 100);
final TaskContainer.TaskProperties taskProperties = getTaskProperty(taskBounds);
final TaskContainer.TaskProperties taskProperties = getTaskProperties(taskBounds);
SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
@@ -400,7 +400,7 @@ public class SplitPresenterTest {
true /* splitHorizontally */);
final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
true /* splitHorizontally */);
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final TaskContainer.TaskProperties taskProperties = getTaskProperties();
SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
.setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
@@ -442,7 +442,7 @@ public class SplitPresenterTest {
false /* splitHorizontally */);
final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
false /* splitHorizontally */);
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final TaskContainer.TaskProperties taskProperties = getTaskProperties();
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(new SplitAttributes.SplitType.HingeSplitType(
SplitAttributes.SplitType.RatioSplitType.splitEqually()
@@ -506,7 +506,7 @@ public class SplitPresenterTest {
@Test
public void testGetRelBoundsForPosition_fallbackToExpandContainers() {
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final TaskContainer.TaskProperties taskProperties = getTaskProperties();
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(new SplitAttributes.SplitType.HingeSplitType(
new SplitAttributes.SplitType.ExpandContainersSplitType()
@@ -528,7 +528,7 @@ public class SplitPresenterTest {
@Test
public void testGetRelBoundsForPosition_useHingeSplitType() {
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final TaskContainer.TaskProperties taskProperties = getTaskProperties();
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(new SplitAttributes.SplitType.HingeSplitType(
new SplitAttributes.SplitType.ExpandContainersSplitType()
@@ -581,13 +581,13 @@ public class SplitPresenterTest {
splitContainer, mActivity, secondaryActivity, null /* secondaryIntent */));
verify(mPresenter, never()).expandTaskFragment(any(), any());
splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES);
splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES);
doReturn(createActivityInfoWithMinDimensions()).when(secondaryActivity).getActivityInfo();
assertEquals(RESULT_EXPAND_FAILED_NO_TF_INFO, mPresenter.expandSplitContainerIfNeeded(
mTransaction, splitContainer, mActivity, secondaryActivity,
null /* secondaryIntent */));
splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES);
splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES);
primaryTf.setInfo(mTransaction, createMockTaskFragmentInfo(primaryTf, mActivity));
secondaryTf.setInfo(mTransaction,
createMockTaskFragmentInfo(secondaryTf, secondaryActivity));
@@ -597,7 +597,7 @@ public class SplitPresenterTest {
verify(mPresenter).expandTaskFragment(mTransaction, primaryTf.getTaskFragmentToken());
verify(mPresenter).expandTaskFragment(mTransaction, secondaryTf.getTaskFragmentToken());
splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES);
splitContainer.updateCurrentSplitAttributes(SPLIT_ATTRIBUTES);
clearInvocations(mPresenter);
assertEquals(RESULT_EXPANDED, mPresenter.expandSplitContainerIfNeeded(mTransaction,
@@ -639,37 +639,35 @@ public class SplitPresenterTest {
.setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.build();
final TaskContainer.TaskProperties taskProperties = getTaskProperty();
final TaskContainer.TaskProperties taskProperties = getTaskProperties();
assertEquals(SPLIT_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties,
splitPairRule, null /* minDimensionsPair */));
splitPairRule, SPLIT_ATTRIBUTES, null /* minDimensionsPair */));
final Pair<Size, Size> minDimensionsPair = new Pair<>(
new Size(TASK_BOUNDS.width(), TASK_BOUNDS.height()), null);
assertEquals(EXPAND_CONTAINERS_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties,
splitPairRule, minDimensionsPair));
splitPairRule, SPLIT_ATTRIBUTES, minDimensionsPair));
taskProperties.getConfiguration().windowConfiguration.setBounds(new Rect(
TASK_BOUNDS.left + 1, TASK_BOUNDS.top + 1, TASK_BOUNDS.right + 1,
TASK_BOUNDS.bottom + 1));
assertEquals(EXPAND_CONTAINERS_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties,
splitPairRule, null /* minDimensionsPair */));
splitPairRule, SPLIT_ATTRIBUTES, null /* minDimensionsPair */));
final SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(
new SplitAttributes.SplitType.HingeSplitType(
SplitAttributes.SplitType.RatioSplitType.splitEqually()
)
).build();
.setSplitType(new SplitAttributes.SplitType.HingeSplitType(
SplitAttributes.SplitType.RatioSplitType.splitEqually()))
.build();
final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
params -> splitAttributes;
mController.setSplitAttributesCalculator(calculator);
assertEquals(splitAttributes, mPresenter.computeSplitAttributes(taskProperties,
splitPairRule, null /* minDimensionsPair */));
splitPairRule, SPLIT_ATTRIBUTES, null /* minDimensionsPair */));
}
@Test
@@ -696,14 +694,15 @@ public class SplitPresenterTest {
doReturn(activityConfig).when(mActivityResources).getConfiguration();
doReturn(new ActivityInfo()).when(activity).getActivityInfo();
doReturn(mock(IBinder.class)).when(activity).getActivityToken();
doReturn(TASK_ID).when(activity).getTaskId();
return activity;
}
private static TaskContainer.TaskProperties getTaskProperty() {
return getTaskProperty(TASK_BOUNDS);
private static TaskContainer.TaskProperties getTaskProperties() {
return getTaskProperties(TASK_BOUNDS);
}
private static TaskContainer.TaskProperties getTaskProperty(@NonNull Rect taskBounds) {
private static TaskContainer.TaskProperties getTaskProperties(@NonNull Rect taskBounds) {
final Configuration configuration = new Configuration();
configuration.windowConfiguration.setBounds(taskBounds);
return new TaskContainer.TaskProperties(DEFAULT_DISPLAY, configuration);