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 static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 20 import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR; 21 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; 22 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; 23 import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK; 24 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION; 25 import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED; 26 27 import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior; 28 import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior; 29 import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked; 30 import static androidx.window.extensions.embedding.SplitContainer.shouldFinishPrimaryWithSecondary; 31 import static androidx.window.extensions.embedding.SplitContainer.shouldFinishSecondaryWithPrimary; 32 33 import android.annotation.ColorInt; 34 import android.app.Activity; 35 import android.app.WindowConfiguration.WindowingMode; 36 import android.content.Intent; 37 import android.graphics.Rect; 38 import android.os.Bundle; 39 import android.os.IBinder; 40 import android.util.ArrayMap; 41 import android.window.TaskFragmentAnimationParams; 42 import android.window.TaskFragmentCreationParams; 43 import android.window.TaskFragmentInfo; 44 import android.window.TaskFragmentOperation; 45 import android.window.TaskFragmentOrganizer; 46 import android.window.TaskFragmentTransaction; 47 import android.window.WindowContainerTransaction; 48 49 import androidx.annotation.NonNull; 50 import androidx.annotation.Nullable; 51 52 import com.android.internal.annotations.VisibleForTesting; 53 import com.android.window.flags.Flags; 54 55 import java.util.Map; 56 import java.util.concurrent.Executor; 57 58 /** 59 * Platform default Extensions implementation of {@link TaskFragmentOrganizer} to organize 60 * task fragments. 61 * 62 * All calls into methods of this class are expected to be on the UI thread. 63 */ 64 class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { 65 66 /** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */ 67 @VisibleForTesting 68 final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>(); 69 70 @NonNull 71 private final TaskFragmentCallback mCallback; 72 73 @VisibleForTesting 74 @Nullable 75 TaskFragmentAnimationController mAnimationController; 76 77 /** 78 * Callback that notifies the controller about changes to task fragments. 79 */ 80 interface TaskFragmentCallback { onTransactionReady(@onNull TaskFragmentTransaction transaction)81 void onTransactionReady(@NonNull TaskFragmentTransaction transaction); 82 } 83 84 /** 85 * @param executor callbacks from WM Core are posted on this executor. It should be tied to the 86 * UI thread that all other calls into methods of this class are also on. 87 */ JetpackTaskFragmentOrganizer(@onNull Executor executor, @NonNull TaskFragmentCallback callback)88 JetpackTaskFragmentOrganizer(@NonNull Executor executor, 89 @NonNull TaskFragmentCallback callback) { 90 super(executor); 91 mCallback = callback; 92 } 93 94 @Override unregisterOrganizer()95 public void unregisterOrganizer() { 96 if (mAnimationController != null) { 97 mAnimationController.unregisterRemoteAnimations(); 98 mAnimationController = null; 99 } 100 super.unregisterOrganizer(); 101 } 102 103 /** 104 * Overrides the animation for transitions of embedded activities organized by this organizer. 105 */ overrideSplitAnimation()106 void overrideSplitAnimation() { 107 if (mAnimationController == null) { 108 mAnimationController = new TaskFragmentAnimationController(this); 109 } 110 mAnimationController.registerRemoteAnimations(); 111 } 112 113 /** 114 * Starts a new Activity and puts it into split with an existing Activity side-by-side. 115 * @param launchingFragmentToken token for the launching TaskFragment. If it exists, it will 116 * be resized based on {@param launchingFragmentBounds}. 117 * Otherwise, we will create a new TaskFragment with the given 118 * token for the {@param launchingActivity}. 119 * @param launchingRelBounds the initial relative bounds for the launching TaskFragment. 120 * @param launchingActivity the Activity to put on the left hand side of the split as the 121 * primary. 122 * @param secondaryFragmentToken token to create the secondary TaskFragment with. 123 * @param secondaryRelBounds the initial relative bounds for the secondary TaskFragment 124 * @param activityIntent Intent to start the secondary Activity with. 125 * @param activityOptions ActivityOptions to start the secondary Activity with. 126 * @param windowingMode the windowing mode to set for the TaskFragments. 127 * @param splitAttributes the {@link SplitAttributes} to represent the split. 128 */ startActivityToSide(@onNull WindowContainerTransaction wct, @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingRelBounds, @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken, @NonNull Rect secondaryRelBounds, @NonNull Intent activityIntent, @Nullable Bundle activityOptions, @NonNull SplitRule rule, @WindowingMode int windowingMode, @NonNull SplitAttributes splitAttributes)129 void startActivityToSide(@NonNull WindowContainerTransaction wct, 130 @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingRelBounds, 131 @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken, 132 @NonNull Rect secondaryRelBounds, @NonNull Intent activityIntent, 133 @Nullable Bundle activityOptions, @NonNull SplitRule rule, 134 @WindowingMode int windowingMode, @NonNull SplitAttributes splitAttributes) { 135 final IBinder ownerToken = launchingActivity.getActivityToken(); 136 137 // Create or resize the launching TaskFragment. 138 if (mFragmentInfos.containsKey(launchingFragmentToken)) { 139 resizeTaskFragment(wct, launchingFragmentToken, launchingRelBounds); 140 updateWindowingMode(wct, launchingFragmentToken, windowingMode); 141 } else { 142 createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken, 143 launchingRelBounds, windowingMode, launchingActivity); 144 } 145 updateAnimationParams(wct, launchingFragmentToken, splitAttributes); 146 147 // Create a TaskFragment for the secondary activity. 148 final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder( 149 getOrganizerToken(), secondaryFragmentToken, ownerToken) 150 .setInitialRelativeBounds(secondaryRelBounds) 151 .setWindowingMode(windowingMode) 152 // Make sure to set the paired fragment token so that the new TaskFragment will be 153 // positioned right above the paired TaskFragment. 154 // This is needed in case we need to launch a placeholder Activity to split below a 155 // transparent always-expand Activity. 156 .setPairedPrimaryFragmentToken(launchingFragmentToken) 157 .build(); 158 createTaskFragment(wct, fragmentOptions); 159 updateAnimationParams(wct, secondaryFragmentToken, splitAttributes); 160 wct.startActivityInTaskFragment(secondaryFragmentToken, ownerToken, activityIntent, 161 activityOptions); 162 163 // Set adjacent to each other so that the containers below will be invisible. 164 setAdjacentTaskFragmentsWithRule(wct, launchingFragmentToken, secondaryFragmentToken, rule); 165 setCompanionTaskFragment(wct, launchingFragmentToken, secondaryFragmentToken, rule, 166 false /* isStacked */); 167 } 168 169 /** 170 * Expands an existing TaskFragment to fill parent. 171 * @param wct WindowContainerTransaction in which the task fragment should be resized. 172 * @param container the {@link TaskFragmentContainer} to be expanded. 173 */ expandTaskFragment(@onNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container)174 void expandTaskFragment(@NonNull WindowContainerTransaction wct, 175 @NonNull TaskFragmentContainer container) { 176 final IBinder fragmentToken = container.getTaskFragmentToken(); 177 resizeTaskFragment(wct, fragmentToken, new Rect()); 178 clearAdjacentTaskFragments(wct, fragmentToken); 179 updateWindowingMode(wct, fragmentToken, WINDOWING_MODE_UNDEFINED); 180 updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT); 181 } 182 183 /** 184 * Expands an Activity to fill parent by moving it to a new TaskFragment. 185 * @param fragmentToken token to create new TaskFragment with. 186 * @param activity activity to move to the fill-parent TaskFragment. 187 */ expandActivity(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull Activity activity)188 void expandActivity(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, 189 @NonNull Activity activity) { 190 createTaskFragmentAndReparentActivity( 191 wct, fragmentToken, activity.getActivityToken(), new Rect(), 192 WINDOWING_MODE_UNDEFINED, activity); 193 updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT); 194 } 195 196 /** 197 * @param ownerToken The token of the activity that creates this task fragment. It does not 198 * have to be a child of this task fragment, but must belong to the same task. 199 */ createTaskFragment(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect relBounds, @WindowingMode int windowingMode)200 void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, 201 @NonNull IBinder ownerToken, @NonNull Rect relBounds, 202 @WindowingMode int windowingMode) { 203 createTaskFragment(wct, fragmentToken, ownerToken, relBounds, windowingMode, 204 null /* pairedActivityToken */); 205 } 206 207 /** 208 * @param ownerToken The token of the activity that creates this task fragment. It does not 209 * have to be a child of this task fragment, but must belong to the same task. 210 * @param pairedActivityToken The token of the activity that will be reparented to this task 211 * fragment. When it is not {@code null}, the task fragment will be 212 * positioned right above it. 213 */ createTaskFragment(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect relBounds, @WindowingMode int windowingMode, @Nullable IBinder pairedActivityToken)214 void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, 215 @NonNull IBinder ownerToken, @NonNull Rect relBounds, @WindowingMode int windowingMode, 216 @Nullable IBinder pairedActivityToken) { 217 final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder( 218 getOrganizerToken(), fragmentToken, ownerToken) 219 .setInitialRelativeBounds(relBounds) 220 .setWindowingMode(windowingMode) 221 .setPairedActivityToken(pairedActivityToken) 222 .build(); 223 createTaskFragment(wct, fragmentOptions); 224 } 225 createTaskFragment(@onNull WindowContainerTransaction wct, @NonNull TaskFragmentCreationParams fragmentOptions)226 void createTaskFragment(@NonNull WindowContainerTransaction wct, 227 @NonNull TaskFragmentCreationParams fragmentOptions) { 228 if (mFragmentInfos.containsKey(fragmentOptions.getFragmentToken())) { 229 throw new IllegalArgumentException( 230 "There is an existing TaskFragment with fragmentToken=" 231 + fragmentOptions.getFragmentToken()); 232 } 233 wct.createTaskFragment(fragmentOptions); 234 } 235 236 /** 237 * @param ownerToken The token of the activity that creates this task fragment. It does not 238 * have to be a child of this task fragment, but must belong to the same task. 239 */ createTaskFragmentAndReparentActivity(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect relBounds, @WindowingMode int windowingMode, @NonNull Activity activity)240 private void createTaskFragmentAndReparentActivity(@NonNull WindowContainerTransaction wct, 241 @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect relBounds, 242 @WindowingMode int windowingMode, @NonNull Activity activity) { 243 final IBinder reparentActivityToken = activity.getActivityToken(); 244 createTaskFragment(wct, fragmentToken, ownerToken, relBounds, windowingMode, 245 reparentActivityToken); 246 wct.reparentActivityToTaskFragment(fragmentToken, reparentActivityToken); 247 } 248 249 /** 250 * Sets the two given TaskFragments as adjacent to each other with respecting the given 251 * {@link SplitRule} for {@link WindowContainerTransaction.TaskFragmentAdjacentParams}. 252 */ setAdjacentTaskFragmentsWithRule(@onNull WindowContainerTransaction wct, @NonNull IBinder primary, @NonNull IBinder secondary, @NonNull SplitRule splitRule)253 void setAdjacentTaskFragmentsWithRule(@NonNull WindowContainerTransaction wct, 254 @NonNull IBinder primary, @NonNull IBinder secondary, @NonNull SplitRule splitRule) { 255 WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams = null; 256 final boolean finishSecondaryWithPrimary = 257 SplitContainer.shouldFinishSecondaryWithPrimary(splitRule); 258 final boolean finishPrimaryWithSecondary = 259 SplitContainer.shouldFinishPrimaryWithSecondary(splitRule); 260 if (finishSecondaryWithPrimary || finishPrimaryWithSecondary) { 261 adjacentParams = new WindowContainerTransaction.TaskFragmentAdjacentParams(); 262 adjacentParams.setShouldDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary); 263 adjacentParams.setShouldDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary); 264 } 265 setAdjacentTaskFragments(wct, primary, secondary, adjacentParams); 266 } 267 setAdjacentTaskFragments(@onNull WindowContainerTransaction wct, @NonNull IBinder primary, @NonNull IBinder secondary, @Nullable WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams)268 void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, 269 @NonNull IBinder primary, @NonNull IBinder secondary, 270 @Nullable WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams) { 271 wct.setAdjacentTaskFragments(primary, secondary, adjacentParams); 272 } 273 clearAdjacentTaskFragments(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken)274 void clearAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, 275 @NonNull IBinder fragmentToken) { 276 // Clear primary will also clear secondary. 277 wct.clearAdjacentTaskFragments(fragmentToken); 278 } 279 setCompanionTaskFragment(@onNull WindowContainerTransaction wct, @NonNull IBinder primary, @NonNull IBinder secondary, @NonNull SplitRule splitRule, boolean isStacked)280 void setCompanionTaskFragment(@NonNull WindowContainerTransaction wct, 281 @NonNull IBinder primary, @NonNull IBinder secondary, @NonNull SplitRule splitRule, 282 boolean isStacked) { 283 final boolean finishPrimaryWithSecondary; 284 if (isStacked) { 285 finishPrimaryWithSecondary = shouldFinishAssociatedContainerWhenStacked( 286 getFinishPrimaryWithSecondaryBehavior(splitRule)); 287 } else { 288 finishPrimaryWithSecondary = shouldFinishPrimaryWithSecondary(splitRule); 289 } 290 setCompanionTaskFragment(wct, primary, finishPrimaryWithSecondary ? secondary : null); 291 292 final boolean finishSecondaryWithPrimary; 293 if (isStacked) { 294 finishSecondaryWithPrimary = shouldFinishAssociatedContainerWhenStacked( 295 getFinishSecondaryWithPrimaryBehavior(splitRule)); 296 } else { 297 finishSecondaryWithPrimary = shouldFinishSecondaryWithPrimary(splitRule); 298 } 299 setCompanionTaskFragment(wct, secondary, finishSecondaryWithPrimary ? primary : null); 300 } 301 setCompanionTaskFragment(@onNull WindowContainerTransaction wct, @NonNull IBinder primary, @Nullable IBinder secondary)302 void setCompanionTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder primary, 303 @Nullable IBinder secondary) { 304 wct.setCompanionTaskFragment(primary, secondary); 305 } 306 resizeTaskFragment(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @Nullable Rect relBounds)307 void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, 308 @Nullable Rect relBounds) { 309 if (!mFragmentInfos.containsKey(fragmentToken)) { 310 throw new IllegalArgumentException( 311 "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken); 312 } 313 if (relBounds == null) { 314 relBounds = new Rect(); 315 } 316 wct.setRelativeBounds(mFragmentInfos.get(fragmentToken).getToken(), relBounds); 317 } 318 updateWindowingMode(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @WindowingMode int windowingMode)319 void updateWindowingMode(@NonNull WindowContainerTransaction wct, 320 @NonNull IBinder fragmentToken, @WindowingMode int windowingMode) { 321 if (!mFragmentInfos.containsKey(fragmentToken)) { 322 throw new IllegalArgumentException( 323 "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken); 324 } 325 wct.setWindowingMode(mFragmentInfos.get(fragmentToken).getToken(), windowingMode); 326 } 327 328 /** 329 * Updates the {@link TaskFragmentAnimationParams} for the given TaskFragment based on 330 * {@link SplitAttributes}. 331 */ updateAnimationParams(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull SplitAttributes splitAttributes)332 void updateAnimationParams(@NonNull WindowContainerTransaction wct, 333 @NonNull IBinder fragmentToken, @NonNull SplitAttributes splitAttributes) { 334 updateAnimationParams(wct, fragmentToken, createAnimationParamsOrDefault(splitAttributes)); 335 } 336 updateAnimationParams(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull TaskFragmentAnimationParams animationParams)337 void updateAnimationParams(@NonNull WindowContainerTransaction wct, 338 @NonNull IBinder fragmentToken, @NonNull TaskFragmentAnimationParams animationParams) { 339 final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( 340 OP_TYPE_SET_ANIMATION_PARAMS) 341 .setAnimationParams(animationParams) 342 .build(); 343 wct.addTaskFragmentOperation(fragmentToken, operation); 344 } 345 deleteTaskFragment(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken)346 void deleteTaskFragment(@NonNull WindowContainerTransaction wct, 347 @NonNull IBinder fragmentToken) { 348 wct.deleteTaskFragment(fragmentToken); 349 } 350 reorderTaskFragmentToFront(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken)351 void reorderTaskFragmentToFront(@NonNull WindowContainerTransaction wct, 352 @NonNull IBinder fragmentToken) { 353 final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( 354 OP_TYPE_REORDER_TO_FRONT).build(); 355 wct.addTaskFragmentOperation(fragmentToken, operation); 356 } 357 setTaskFragmentIsolatedNavigation(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, boolean isolatedNav)358 void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct, 359 @NonNull IBinder fragmentToken, boolean isolatedNav) { 360 final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( 361 OP_TYPE_SET_ISOLATED_NAVIGATION).setBooleanValue(isolatedNav).build(); 362 wct.addTaskFragmentOperation(fragmentToken, operation); 363 } 364 setTaskFragmentPinned(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, boolean pinned)365 void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct, 366 @NonNull IBinder fragmentToken, boolean pinned) { 367 final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( 368 OP_TYPE_SET_PINNED).setBooleanValue(pinned).build(); 369 wct.addTaskFragmentOperation(fragmentToken, operation); 370 } 371 setTaskFragmentDimOnTask(@onNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, boolean dimOnTask)372 void setTaskFragmentDimOnTask(@NonNull WindowContainerTransaction wct, 373 @NonNull IBinder fragmentToken, boolean dimOnTask) { 374 final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( 375 OP_TYPE_SET_DIM_ON_TASK).setBooleanValue(dimOnTask).build(); 376 wct.addTaskFragmentOperation(fragmentToken, operation); 377 } 378 updateTaskFragmentInfo(@onNull TaskFragmentInfo taskFragmentInfo)379 void updateTaskFragmentInfo(@NonNull TaskFragmentInfo taskFragmentInfo) { 380 mFragmentInfos.put(taskFragmentInfo.getFragmentToken(), taskFragmentInfo); 381 } 382 removeTaskFragmentInfo(@onNull TaskFragmentInfo taskFragmentInfo)383 void removeTaskFragmentInfo(@NonNull TaskFragmentInfo taskFragmentInfo) { 384 mFragmentInfos.remove(taskFragmentInfo.getFragmentToken()); 385 } 386 387 @Override onTransactionReady(@onNull TaskFragmentTransaction transaction)388 public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) { 389 mCallback.onTransactionReady(transaction); 390 } 391 createAnimationParamsOrDefault( @ullable SplitAttributes splitAttributes)392 private static TaskFragmentAnimationParams createAnimationParamsOrDefault( 393 @Nullable SplitAttributes splitAttributes) { 394 if (splitAttributes == null) { 395 return TaskFragmentAnimationParams.DEFAULT; 396 } 397 final TaskFragmentAnimationParams.Builder builder = 398 new TaskFragmentAnimationParams.Builder(); 399 final int animationBackgroundColor = getAnimationBackgroundColor(splitAttributes); 400 builder.setAnimationBackgroundColor(animationBackgroundColor); 401 final int openAnimationResId = 402 splitAttributes.getAnimationParams().getOpenAnimationResId(); 403 builder.setOpenAnimationResId(openAnimationResId); 404 final int closeAnimationResId = 405 splitAttributes.getAnimationParams().getCloseAnimationResId(); 406 builder.setCloseAnimationResId(closeAnimationResId); 407 final int changeAnimationResId = 408 splitAttributes.getAnimationParams().getChangeAnimationResId(); 409 builder.setChangeAnimationResId(changeAnimationResId); 410 return builder.build(); 411 } 412 413 @ColorInt getAnimationBackgroundColor(@onNull SplitAttributes splitAttributes)414 private static int getAnimationBackgroundColor(@NonNull SplitAttributes splitAttributes) { 415 int animationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR; 416 final AnimationBackground animationBackground = 417 splitAttributes.getAnimationParams().getAnimationBackground(); 418 if (animationBackground instanceof AnimationBackground.ColorBackground colorBackground) { 419 animationBackgroundColor = colorBackground.getColor(); 420 } 421 return animationBackgroundColor; 422 } 423 } 424