1 /*
2  * Copyright (C) 2023 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 com.android.wm.shell.back;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.res.Configuration;
22 import android.util.Log;
23 import android.util.SparseArray;
24 import android.window.BackNavigationInfo;
25 
26 /** Registry for all types of default back animations */
27 public class ShellBackAnimationRegistry {
28     private static final String TAG = "ShellBackPreview";
29 
30     private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
31     private ShellBackAnimation mDefaultCrossActivityAnimation;
32     private final ShellBackAnimation mCustomizeActivityAnimation;
33     private final ShellBackAnimation mCrossTaskAnimation;
34 
ShellBackAnimationRegistry( @hellBackAnimation.CrossActivity @ullable ShellBackAnimation crossActivityAnimation, @ShellBackAnimation.CrossTask @Nullable ShellBackAnimation crossTaskAnimation, @ShellBackAnimation.DialogClose @Nullable ShellBackAnimation dialogCloseAnimation, @ShellBackAnimation.CustomizeActivity @Nullable ShellBackAnimation customizeActivityAnimation, @ShellBackAnimation.ReturnToHome @Nullable ShellBackAnimation defaultBackToHomeAnimation)35     public ShellBackAnimationRegistry(
36             @ShellBackAnimation.CrossActivity @Nullable ShellBackAnimation crossActivityAnimation,
37             @ShellBackAnimation.CrossTask @Nullable ShellBackAnimation crossTaskAnimation,
38             @ShellBackAnimation.DialogClose @Nullable ShellBackAnimation dialogCloseAnimation,
39             @ShellBackAnimation.CustomizeActivity @Nullable
40                     ShellBackAnimation customizeActivityAnimation,
41             @ShellBackAnimation.ReturnToHome @Nullable
42                     ShellBackAnimation defaultBackToHomeAnimation) {
43         if (crossActivityAnimation != null) {
44             mAnimationDefinition.set(
45                     BackNavigationInfo.TYPE_CROSS_ACTIVITY, crossActivityAnimation.getRunner());
46         }
47         if (crossTaskAnimation != null) {
48             mAnimationDefinition.set(
49                     BackNavigationInfo.TYPE_CROSS_TASK, crossTaskAnimation.getRunner());
50         }
51         if (dialogCloseAnimation != null) {
52             mAnimationDefinition.set(
53                     BackNavigationInfo.TYPE_DIALOG_CLOSE, dialogCloseAnimation.getRunner());
54         }
55         if (defaultBackToHomeAnimation != null) {
56             mAnimationDefinition.set(
57                     BackNavigationInfo.TYPE_RETURN_TO_HOME, defaultBackToHomeAnimation.getRunner());
58         }
59 
60         mDefaultCrossActivityAnimation = crossActivityAnimation;
61         mCustomizeActivityAnimation = customizeActivityAnimation;
62         mCrossTaskAnimation = crossTaskAnimation;
63 
64         // TODO(b/236760237): register dialog close animation when it's completed.
65     }
66 
registerAnimation( @ackNavigationInfo.BackTargetType int type, @NonNull BackAnimationRunner runner)67     void registerAnimation(
68             @BackNavigationInfo.BackTargetType int type, @NonNull BackAnimationRunner runner) {
69         mAnimationDefinition.set(type, runner);
70         // Only happen in test
71         if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
72             mDefaultCrossActivityAnimation = null;
73         }
74     }
75 
unregisterAnimation(@ackNavigationInfo.BackTargetType int type)76     void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
77         mAnimationDefinition.remove(type);
78         // Only happen in test
79         if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
80             mDefaultCrossActivityAnimation = null;
81         }
82     }
83 
84     /**
85      * Start the {@link BackAnimationRunner} associated with a back target type.
86      *
87      * @param type back target type
88      * @return true if the animation is started, false if animation is not found for that type.
89      */
startGesture(@ackNavigationInfo.BackTargetType int type)90     boolean startGesture(@BackNavigationInfo.BackTargetType int type) {
91         BackAnimationRunner runner = mAnimationDefinition.get(type);
92         if (runner == null) {
93             return false;
94         }
95         runner.startGesture();
96         return true;
97     }
98 
99     /**
100      * Cancel the {@link BackAnimationRunner} associated with a back target type.
101      *
102      * @param type back target type
103      * @return true if the animation is started, false if animation is not found for that type.
104      */
cancel(@ackNavigationInfo.BackTargetType int type)105     boolean cancel(@BackNavigationInfo.BackTargetType int type) {
106         BackAnimationRunner runner = mAnimationDefinition.get(type);
107         if (runner == null) {
108             return false;
109         }
110         runner.cancelAnimation();
111         return true;
112     }
113 
isAnimationCancelledOrNull(@ackNavigationInfo.BackTargetType int type)114     boolean isAnimationCancelledOrNull(@BackNavigationInfo.BackTargetType int type) {
115         BackAnimationRunner runner = mAnimationDefinition.get(type);
116         if (runner == null) {
117             return true;
118         }
119         return runner.isAnimationCancelled();
120     }
121 
isWaitingAnimation(@ackNavigationInfo.BackTargetType int type)122     boolean isWaitingAnimation(@BackNavigationInfo.BackTargetType int type) {
123         BackAnimationRunner runner = mAnimationDefinition.get(type);
124         if (runner == null) {
125             return false;
126         }
127         return runner.isWaitingAnimation();
128     }
129 
resetDefaultCrossActivity()130     void resetDefaultCrossActivity() {
131         if (mDefaultCrossActivityAnimation == null
132                 || !mAnimationDefinition.contains(BackNavigationInfo.TYPE_CROSS_ACTIVITY)) {
133             return;
134         }
135         mAnimationDefinition.set(
136                 BackNavigationInfo.TYPE_CROSS_ACTIVITY, mDefaultCrossActivityAnimation.getRunner());
137     }
138 
onConfigurationChanged(Configuration newConfig)139     void onConfigurationChanged(Configuration newConfig) {
140         if (mCustomizeActivityAnimation != null) {
141             mCustomizeActivityAnimation.onConfigurationChanged(newConfig);
142         }
143         if (mDefaultCrossActivityAnimation != null) {
144             mDefaultCrossActivityAnimation.onConfigurationChanged(newConfig);
145         }
146         if (mCrossTaskAnimation != null) {
147             mCrossTaskAnimation.onConfigurationChanged(newConfig);
148         }
149     }
150 
getAnimationRunnerAndInit(BackNavigationInfo backNavigationInfo)151     BackAnimationRunner getAnimationRunnerAndInit(BackNavigationInfo backNavigationInfo) {
152         int type = backNavigationInfo.getType();
153         // Initiate customized cross-activity animation, or fall back to cross activity animation
154         if (type == BackNavigationInfo.TYPE_CROSS_ACTIVITY && mAnimationDefinition.contains(type)) {
155             if (mCustomizeActivityAnimation != null
156                     && mCustomizeActivityAnimation.prepareNextAnimation(
157                             backNavigationInfo.getCustomAnimationInfo(), 0)) {
158                 mAnimationDefinition.get(type).resetWaitingAnimation();
159                 mAnimationDefinition.set(
160                         BackNavigationInfo.TYPE_CROSS_ACTIVITY,
161                         mCustomizeActivityAnimation.getRunner());
162             } else if (mDefaultCrossActivityAnimation != null) {
163                 mDefaultCrossActivityAnimation.prepareNextAnimation(null,
164                         backNavigationInfo.getLetterboxColor());
165             }
166         }
167         BackAnimationRunner runner = mAnimationDefinition.get(type);
168         if (runner == null) {
169             Log.e(
170                     TAG,
171                     "Animation didn't be defined for type "
172                             + BackNavigationInfo.typeToString(type));
173         }
174         return runner;
175     }
176 }
177