• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.window.extensions.embedding;
18 
19 import android.app.Activity;
20 import android.os.Binder;
21 import android.os.IBinder;
22 import android.util.Pair;
23 import android.util.Size;
24 import android.window.TaskFragmentParentInfo;
25 import android.window.WindowContainerTransaction;
26 
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 import androidx.window.extensions.core.util.function.Function;
30 
31 /**
32  * Client-side descriptor of a split that holds two containers.
33  */
34 class SplitContainer {
35     @NonNull
36     private final ParcelableSplitContainerData mParcelableData;
37     @NonNull
38     private TaskFragmentContainer mPrimaryContainer;
39     @NonNull
40     private final TaskFragmentContainer mSecondaryContainer;
41     @NonNull
42     private final SplitRule mSplitRule;
43     /** @see SplitContainer#getCurrentSplitAttributes() */
44     @NonNull
45     private SplitAttributes mCurrentSplitAttributes;
46     /** @see SplitContainer#getDefaultSplitAttributes() */
47     @NonNull
48     private SplitAttributes mDefaultSplitAttributes;
49 
SplitContainer(@onNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes)50     SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
51             @NonNull Activity primaryActivity,
52             @NonNull TaskFragmentContainer secondaryContainer,
53             @NonNull SplitRule splitRule,
54             @NonNull SplitAttributes splitAttributes) {
55         this(primaryContainer, primaryActivity, secondaryContainer, splitRule, splitAttributes,
56                 false /* isPrimaryContainerMutable */);
57     }
58 
SplitContainer(@onNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable)59     SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
60             @NonNull Activity primaryActivity,
61             @NonNull TaskFragmentContainer secondaryContainer,
62             @NonNull SplitRule splitRule,
63             @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) {
64         mParcelableData = new ParcelableSplitContainerData(this, new Binder("SplitContainer"),
65                 primaryContainer.getToken(), secondaryContainer.getToken(), splitRule.getTag(),
66                 isPrimaryContainerMutable);
67         mPrimaryContainer = primaryContainer;
68         mSecondaryContainer = secondaryContainer;
69         mSplitRule = splitRule;
70         mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
71         mCurrentSplitAttributes = splitAttributes;
72 
73         if (shouldFinishPrimaryWithSecondary(splitRule)) {
74             if (mPrimaryContainer.getRunningActivityCount() == 1
75                     && mPrimaryContainer.hasActivity(primaryActivity.getActivityToken())) {
76                 mSecondaryContainer.addContainerToFinishOnExit(mPrimaryContainer);
77             } else {
78                 // Only adding the activity to be finished vs. the entire TaskFragment while
79                 // the secondary container exits because there are other unrelated activities in the
80                 // primary TaskFragment.
81                 mSecondaryContainer.addActivityToFinishOnExit(primaryActivity);
82             }
83         }
84         if (shouldFinishSecondaryWithPrimary(splitRule)) {
85             mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
86         }
87     }
88 
89     /** This is only used when restoring it from a {@link ParcelableSplitContainerData}. */
SplitContainer(@onNull ParcelableSplitContainerData parcelableData, @NonNull SplitController splitController, @NonNull SplitRule splitRule)90     SplitContainer(@NonNull ParcelableSplitContainerData parcelableData,
91             @NonNull SplitController splitController, @NonNull SplitRule splitRule) {
92         mParcelableData = parcelableData;
93         mPrimaryContainer = splitController.getContainer(parcelableData.getPrimaryContainerToken());
94         mSecondaryContainer = splitController.getContainer(
95                 parcelableData.getSecondaryContainerToken());
96         mSplitRule = splitRule;
97         mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
98         mCurrentSplitAttributes = mDefaultSplitAttributes;
99 
100         if (shouldFinishPrimaryWithSecondary(splitRule)) {
101             addContainerToFinishOnExitWhenRestore(mSecondaryContainer, mPrimaryContainer);
102         }
103         if (shouldFinishSecondaryWithPrimary(splitRule)) {
104             addContainerToFinishOnExitWhenRestore(mPrimaryContainer, mSecondaryContainer);
105         }
106     }
107 
addContainerToFinishOnExitWhenRestore( @onNull TaskFragmentContainer containerToAdd, @NonNull TaskFragmentContainer containerToFinish)108     private void addContainerToFinishOnExitWhenRestore(
109             @NonNull TaskFragmentContainer containerToAdd,
110             @NonNull TaskFragmentContainer containerToFinish) {
111         // If an activity was already added to be finished after the restoration, then that's it.
112         // Otherwise, add the container to finish on exit.
113         if (!containerToAdd.hasActivityToFinishOnExit(containerToFinish)) {
114             containerToAdd.addContainerToFinishOnExit(containerToFinish);
115         }
116     }
117 
setPrimaryContainer(@onNull TaskFragmentContainer primaryContainer)118     void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) {
119         if (!mParcelableData.mIsPrimaryContainerMutable) {
120             throw new IllegalStateException("Cannot update primary TaskFragmentContainer");
121         }
122         mPrimaryContainer = primaryContainer;
123     }
124 
125     @NonNull
getPrimaryContainer()126     TaskFragmentContainer getPrimaryContainer() {
127         return mPrimaryContainer;
128     }
129 
130     @NonNull
getSecondaryContainer()131     TaskFragmentContainer getSecondaryContainer() {
132         return mSecondaryContainer;
133     }
134 
135     @NonNull
getSplitRule()136     SplitRule getSplitRule() {
137         return mSplitRule;
138     }
139 
140     /**
141      * Returns the current {@link SplitAttributes} this {@code SplitContainer} is showing.
142      * <p>
143      * If the {@code SplitAttributes} calculator function is not set by
144      * {@link SplitController#setSplitAttributesCalculator(Function)}, the current
145      * {@code SplitAttributes} is either to expand the containers if the size constraints of
146      * {@link #getSplitRule()} are not satisfied,
147      * or the {@link #getDefaultSplitAttributes()}, otherwise.
148      * </p><p>
149      * If the {@code SplitAttributes} calculator function is set, the current
150      * {@code SplitAttributes} will be customized by the function, which can be any
151      * {@code SplitAttributes}.
152      * </p>
153      *
154      * @see SplitAttributes.SplitType.ExpandContainersSplitType
155      */
156     @NonNull
getCurrentSplitAttributes()157     SplitAttributes getCurrentSplitAttributes() {
158         return mCurrentSplitAttributes;
159     }
160 
161     /**
162      * Returns the default {@link SplitAttributes} when the parent task container bounds satisfy
163      * {@link #getSplitRule()} constraints.
164      * <p>
165      * The value is usually from {@link SplitRule#getDefaultSplitAttributes} unless it is overridden
166      * by {@link SplitController#updateSplitAttributes(IBinder, SplitAttributes)}.
167      */
168     @NonNull
getDefaultSplitAttributes()169     SplitAttributes getDefaultSplitAttributes() {
170         return mDefaultSplitAttributes;
171     }
172 
173     @NonNull
getToken()174     IBinder getToken() {
175         return mParcelableData.mToken;
176     }
177 
178     @NonNull
getParcelableData()179     ParcelableSplitContainerData getParcelableData() {
180         return mParcelableData;
181     }
182 
183     /**
184      * Updates the {@link SplitAttributes} to this container.
185      * It is usually used when there's a folding state change or
186      * {@link SplitController#onTaskFragmentParentInfoChanged(WindowContainerTransaction,
187      * int, TaskFragmentParentInfo)}.
188      */
updateCurrentSplitAttributes(@onNull SplitAttributes splitAttributes)189     void updateCurrentSplitAttributes(@NonNull SplitAttributes splitAttributes) {
190         mCurrentSplitAttributes = splitAttributes;
191     }
192 
193     /**
194      * Overrides the default {@link SplitAttributes} to this container, which may be different
195      * from {@link SplitRule#getDefaultSplitAttributes}.
196      */
updateDefaultSplitAttributes(@onNull SplitAttributes splitAttributes)197     void updateDefaultSplitAttributes(@NonNull SplitAttributes splitAttributes) {
198         mDefaultSplitAttributes = splitAttributes;
199     }
200 
201     @NonNull
getTaskContainer()202     TaskContainer getTaskContainer() {
203         return getPrimaryContainer().getTaskContainer();
204     }
205 
206     /** Returns the minimum dimension pair of primary container and secondary container. */
207     @NonNull
getMinDimensionsPair()208     Pair<Size, Size> getMinDimensionsPair() {
209         return new Pair<>(mPrimaryContainer.getMinDimensions(),
210                 mSecondaryContainer.getMinDimensions());
211     }
212 
isPlaceholderContainer()213     boolean isPlaceholderContainer() {
214         return (mSplitRule instanceof SplitPlaceholderRule);
215     }
216 
217     /**
218      * Returns the SplitInfo representing this container.
219      *
220      * @return the SplitInfo representing this container if the underlying TaskFragmentContainers
221      * are stable, or {@code null} if any TaskFragmentContainer is in an intermediate state.
222      */
223     @Nullable
toSplitInfoIfStable()224     SplitInfo toSplitInfoIfStable() {
225         final ActivityStack primaryActivityStack = mPrimaryContainer.toActivityStackIfStable();
226         final ActivityStack secondaryActivityStack = mSecondaryContainer.toActivityStackIfStable();
227         if (primaryActivityStack == null || secondaryActivityStack == null) {
228             return null;
229         }
230         return new SplitInfo(primaryActivityStack, secondaryActivityStack,
231                 mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mParcelableData.mToken));
232     }
233 
shouldFinishPrimaryWithSecondary(@onNull SplitRule splitRule)234     static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
235         final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
236         final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule)
237                 && ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary()
238                 != SplitRule.FINISH_NEVER;
239         return shouldFinishPrimaryWithSecondary || isPlaceholderContainer;
240     }
241 
shouldFinishSecondaryWithPrimary(@onNull SplitRule splitRule)242     static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) {
243         final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
244         final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule)
245                 && ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary()
246                 != SplitRule.FINISH_NEVER;
247         return shouldFinishSecondaryWithPrimary || isPlaceholderContainer;
248     }
249 
shouldFinishAssociatedContainerWhenStacked(int finishBehavior)250     static boolean shouldFinishAssociatedContainerWhenStacked(int finishBehavior) {
251         return finishBehavior == SplitRule.FINISH_ALWAYS;
252     }
253 
shouldFinishAssociatedContainerWhenAdjacent(int finishBehavior)254     static boolean shouldFinishAssociatedContainerWhenAdjacent(int finishBehavior) {
255         return finishBehavior == SplitRule.FINISH_ALWAYS
256                 || finishBehavior == SplitRule.FINISH_ADJACENT;
257     }
258 
getFinishPrimaryWithSecondaryBehavior(@onNull SplitRule splitRule)259     static int getFinishPrimaryWithSecondaryBehavior(@NonNull SplitRule splitRule) {
260         if (splitRule instanceof SplitPlaceholderRule) {
261             return ((SplitPlaceholderRule) splitRule).getFinishPrimaryWithSecondary();
262         }
263         if (splitRule instanceof SplitPairRule) {
264             return ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary();
265         }
266         return SplitRule.FINISH_NEVER;
267     }
268 
getFinishSecondaryWithPrimaryBehavior(@onNull SplitRule splitRule)269     static int getFinishSecondaryWithPrimaryBehavior(@NonNull SplitRule splitRule) {
270         if (splitRule instanceof SplitPlaceholderRule) {
271             return SplitRule.FINISH_ALWAYS;
272         }
273         if (splitRule instanceof SplitPairRule) {
274             return ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary();
275         }
276         return SplitRule.FINISH_NEVER;
277     }
278 
isStickyPlaceholderRule(@onNull SplitRule splitRule)279     static boolean isStickyPlaceholderRule(@NonNull SplitRule splitRule) {
280         if (!(splitRule instanceof SplitPlaceholderRule)) {
281             return false;
282         }
283         return ((SplitPlaceholderRule) splitRule).isSticky();
284     }
285 
286     @Override
toString()287     public String toString() {
288         return "SplitContainer{"
289                 + " primaryContainer=" + mPrimaryContainer
290                 + ", secondaryContainer=" + mSecondaryContainer
291                 + ", splitRule=" + mSplitRule
292                 + ", currentSplitAttributes" + mCurrentSplitAttributes
293                 + ", defaultSplitAttributes" + mDefaultSplitAttributes
294                 + "}";
295     }
296 }
297