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:
@@ -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
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user