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