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