• 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 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