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 TaskFragmentContainer mPrimaryContainer; 37 @NonNull 38 private final TaskFragmentContainer mSecondaryContainer; 39 @NonNull 40 private final SplitRule mSplitRule; 41 /** @see SplitContainer#getCurrentSplitAttributes() */ 42 @NonNull 43 private SplitAttributes mCurrentSplitAttributes; 44 /** @see SplitContainer#getDefaultSplitAttributes() */ 45 @NonNull 46 private SplitAttributes mDefaultSplitAttributes; 47 @NonNull 48 private final IBinder mToken; 49 50 /** 51 * Whether the selection of which container is primary can be changed at runtime. Runtime 52 * updates is currently possible only for {@link SplitPinContainer} 53 * 54 * @see SplitPinContainer 55 */ 56 private final boolean mIsPrimaryContainerMutable; 57 SplitContainer(@onNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes)58 SplitContainer(@NonNull TaskFragmentContainer primaryContainer, 59 @NonNull Activity primaryActivity, 60 @NonNull TaskFragmentContainer secondaryContainer, 61 @NonNull SplitRule splitRule, 62 @NonNull SplitAttributes splitAttributes) { 63 this(primaryContainer, primaryActivity, secondaryContainer, splitRule, splitAttributes, 64 false /* isPrimaryContainerMutable */); 65 } 66 SplitContainer(@onNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable)67 SplitContainer(@NonNull TaskFragmentContainer primaryContainer, 68 @NonNull Activity primaryActivity, 69 @NonNull TaskFragmentContainer secondaryContainer, 70 @NonNull SplitRule splitRule, 71 @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) { 72 mPrimaryContainer = primaryContainer; 73 mSecondaryContainer = secondaryContainer; 74 mSplitRule = splitRule; 75 mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); 76 mCurrentSplitAttributes = splitAttributes; 77 mToken = new Binder("SplitContainer"); 78 mIsPrimaryContainerMutable = isPrimaryContainerMutable; 79 80 if (shouldFinishPrimaryWithSecondary(splitRule)) { 81 if (mPrimaryContainer.getRunningActivityCount() == 1 82 && mPrimaryContainer.hasActivity(primaryActivity.getActivityToken())) { 83 mSecondaryContainer.addContainerToFinishOnExit(mPrimaryContainer); 84 } else { 85 // Only adding the activity to be finished vs. the entire TaskFragment while 86 // the secondary container exits because there are other unrelated activities in the 87 // primary TaskFragment. 88 mSecondaryContainer.addActivityToFinishOnExit(primaryActivity); 89 } 90 } 91 if (shouldFinishSecondaryWithPrimary(splitRule)) { 92 mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer); 93 } 94 } 95 setPrimaryContainer(@onNull TaskFragmentContainer primaryContainer)96 void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { 97 if (!mIsPrimaryContainerMutable) { 98 throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); 99 } 100 mPrimaryContainer = primaryContainer; 101 } 102 103 @NonNull getPrimaryContainer()104 TaskFragmentContainer getPrimaryContainer() { 105 return mPrimaryContainer; 106 } 107 108 @NonNull getSecondaryContainer()109 TaskFragmentContainer getSecondaryContainer() { 110 return mSecondaryContainer; 111 } 112 113 @NonNull getSplitRule()114 SplitRule getSplitRule() { 115 return mSplitRule; 116 } 117 118 /** 119 * Returns the current {@link SplitAttributes} this {@code SplitContainer} is showing. 120 * <p> 121 * If the {@code SplitAttributes} calculator function is not set by 122 * {@link SplitController#setSplitAttributesCalculator(Function)}, the current 123 * {@code SplitAttributes} is either to expand the containers if the size constraints of 124 * {@link #getSplitRule()} are not satisfied, 125 * or the {@link #getDefaultSplitAttributes()}, otherwise. 126 * </p><p> 127 * If the {@code SplitAttributes} calculator function is set, the current 128 * {@code SplitAttributes} will be customized by the function, which can be any 129 * {@code SplitAttributes}. 130 * </p> 131 * 132 * @see SplitAttributes.SplitType.ExpandContainersSplitType 133 */ 134 @NonNull getCurrentSplitAttributes()135 SplitAttributes getCurrentSplitAttributes() { 136 return mCurrentSplitAttributes; 137 } 138 139 /** 140 * Returns the default {@link SplitAttributes} when the parent task container bounds satisfy 141 * {@link #getSplitRule()} constraints. 142 * <p> 143 * The value is usually from {@link SplitRule#getDefaultSplitAttributes} unless it is overridden 144 * by {@link SplitController#updateSplitAttributes(IBinder, SplitAttributes)}. 145 */ 146 @NonNull getDefaultSplitAttributes()147 SplitAttributes getDefaultSplitAttributes() { 148 return mDefaultSplitAttributes; 149 } 150 151 @NonNull getToken()152 IBinder getToken() { 153 return mToken; 154 } 155 156 /** 157 * Updates the {@link SplitAttributes} to this container. 158 * It is usually used when there's a folding state change or 159 * {@link SplitController#onTaskFragmentParentInfoChanged(WindowContainerTransaction, 160 * int, TaskFragmentParentInfo)}. 161 */ updateCurrentSplitAttributes(@onNull SplitAttributes splitAttributes)162 void updateCurrentSplitAttributes(@NonNull SplitAttributes splitAttributes) { 163 mCurrentSplitAttributes = splitAttributes; 164 } 165 166 /** 167 * Overrides the default {@link SplitAttributes} to this container, which may be different 168 * from {@link SplitRule#getDefaultSplitAttributes}. 169 */ updateDefaultSplitAttributes(@onNull SplitAttributes splitAttributes)170 void updateDefaultSplitAttributes(@NonNull SplitAttributes splitAttributes) { 171 mDefaultSplitAttributes = splitAttributes; 172 } 173 174 @NonNull getTaskContainer()175 TaskContainer getTaskContainer() { 176 return getPrimaryContainer().getTaskContainer(); 177 } 178 179 /** Returns the minimum dimension pair of primary container and secondary container. */ 180 @NonNull getMinDimensionsPair()181 Pair<Size, Size> getMinDimensionsPair() { 182 return new Pair<>(mPrimaryContainer.getMinDimensions(), 183 mSecondaryContainer.getMinDimensions()); 184 } 185 isPlaceholderContainer()186 boolean isPlaceholderContainer() { 187 return (mSplitRule instanceof SplitPlaceholderRule); 188 } 189 190 /** 191 * Returns the SplitInfo representing this container. 192 * 193 * @return the SplitInfo representing this container if the underlying TaskFragmentContainers 194 * are stable, or {@code null} if any TaskFragmentContainer is in an intermediate state. 195 */ 196 @Nullable toSplitInfoIfStable()197 SplitInfo toSplitInfoIfStable() { 198 final ActivityStack primaryActivityStack = mPrimaryContainer.toActivityStackIfStable(); 199 final ActivityStack secondaryActivityStack = mSecondaryContainer.toActivityStackIfStable(); 200 if (primaryActivityStack == null || secondaryActivityStack == null) { 201 return null; 202 } 203 return new SplitInfo(primaryActivityStack, secondaryActivityStack, 204 mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken)); 205 } 206 shouldFinishPrimaryWithSecondary(@onNull SplitRule splitRule)207 static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { 208 final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule; 209 final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule) 210 && ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary() 211 != SplitRule.FINISH_NEVER; 212 return shouldFinishPrimaryWithSecondary || isPlaceholderContainer; 213 } 214 shouldFinishSecondaryWithPrimary(@onNull SplitRule splitRule)215 static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) { 216 final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule; 217 final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule) 218 && ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary() 219 != SplitRule.FINISH_NEVER; 220 return shouldFinishSecondaryWithPrimary || isPlaceholderContainer; 221 } 222 shouldFinishAssociatedContainerWhenStacked(int finishBehavior)223 static boolean shouldFinishAssociatedContainerWhenStacked(int finishBehavior) { 224 return finishBehavior == SplitRule.FINISH_ALWAYS; 225 } 226 shouldFinishAssociatedContainerWhenAdjacent(int finishBehavior)227 static boolean shouldFinishAssociatedContainerWhenAdjacent(int finishBehavior) { 228 return finishBehavior == SplitRule.FINISH_ALWAYS 229 || finishBehavior == SplitRule.FINISH_ADJACENT; 230 } 231 getFinishPrimaryWithSecondaryBehavior(@onNull SplitRule splitRule)232 static int getFinishPrimaryWithSecondaryBehavior(@NonNull SplitRule splitRule) { 233 if (splitRule instanceof SplitPlaceholderRule) { 234 return ((SplitPlaceholderRule) splitRule).getFinishPrimaryWithSecondary(); 235 } 236 if (splitRule instanceof SplitPairRule) { 237 return ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary(); 238 } 239 return SplitRule.FINISH_NEVER; 240 } 241 getFinishSecondaryWithPrimaryBehavior(@onNull SplitRule splitRule)242 static int getFinishSecondaryWithPrimaryBehavior(@NonNull SplitRule splitRule) { 243 if (splitRule instanceof SplitPlaceholderRule) { 244 return SplitRule.FINISH_ALWAYS; 245 } 246 if (splitRule instanceof SplitPairRule) { 247 return ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary(); 248 } 249 return SplitRule.FINISH_NEVER; 250 } 251 isStickyPlaceholderRule(@onNull SplitRule splitRule)252 static boolean isStickyPlaceholderRule(@NonNull SplitRule splitRule) { 253 if (!(splitRule instanceof SplitPlaceholderRule)) { 254 return false; 255 } 256 return ((SplitPlaceholderRule) splitRule).isSticky(); 257 } 258 259 @Override toString()260 public String toString() { 261 return "SplitContainer{" 262 + " primaryContainer=" + mPrimaryContainer 263 + ", secondaryContainer=" + mSecondaryContainer 264 + ", splitRule=" + mSplitRule 265 + ", currentSplitAttributes" + mCurrentSplitAttributes 266 + ", defaultSplitAttributes" + mDefaultSplitAttributes 267 + "}"; 268 } 269 } 270