• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 package com.android.car.scalableui.model;
17 
18 import static android.view.Display.DEFAULT_DISPLAY;
19 
20 import android.animation.Animator;
21 
22 import androidx.annotation.NonNull;
23 import androidx.annotation.Nullable;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.stream.Collectors;
28 
29 /**
30  * Represents the state of a panel in the Scalable UI system.
31  *
32  * <p>A PanelState defines the different variants (layouts) that a panel can have, as well as the
33  * transitions between those variants. It also manages the current variant and any running
34  * animations.
35  */
36 public class PanelState {
37     private static final String TAG = PanelState.class.getSimpleName();
38 
39     public static final String DEFAULT_ROLE = "DEFAULT";
40     public static final String DECOR_PANEL_ID_PREFIX = "decor";
41 
42     private String mDefaultVariant;
43     private int mDisplayId;
44 
45     private final String mId;
46     private final Role mRole;
47     private final List<Variant> mVariants = new ArrayList<>();
48     private final List<Transition> mTransitions = new ArrayList<>();
49 
50     @Nullable private Animator mRunningAnimator;
51     @Nullable private Variant mCurrentVariant;
52 
53     /**
54      * Constructor for PanelState.
55      *
56      * @param id The ID of the panel.
57      * @param role The role of the panel.
58      */
PanelState(@onNull String id, @NonNull Role role)59     public PanelState(@NonNull String id, @NonNull Role role) {
60         mId = id;
61         mRole = role;
62         mDisplayId = DEFAULT_DISPLAY;
63     }
64 
65     /**
66      * Constructor to copy a PanelState
67      */
PanelState(@onNull PanelState other)68     public PanelState(@NonNull PanelState other) {
69         mId = other.mId;
70         mRole = other.mRole;
71         mDisplayId = other.mDisplayId;
72         mDefaultVariant = other.mDefaultVariant;
73         mVariants.addAll(other.mVariants);
74         mTransitions.addAll(other.mTransitions);
75         mRunningAnimator = other.mRunningAnimator;
76         mCurrentVariant = other.mCurrentVariant;
77     }
78 
79     /** Returns id */
80     @NonNull
getId()81     public String getId() {
82         return mId;
83     }
84 
85     /** Adds variant */
addVariant(@onNull Variant variant)86     public void addVariant(@NonNull Variant variant) {
87         mVariants.add(variant);
88     }
89 
90     /** Adds transition */
addTransition(@onNull Transition transition)91     public void addTransition(@NonNull Transition transition) {
92         mTransitions.add(transition);
93     }
94 
95     /** Returns current variant */
96     @Nullable
getCurrentVariant()97     public Variant getCurrentVariant() {
98         if (mCurrentVariant == null) {
99             // Ensure mVariants is not empty before accessing
100             if (!mVariants.isEmpty()) {
101                 mCurrentVariant = mVariants.get(0);
102             }
103         }
104         return mCurrentVariant;
105     }
106 
107     /** Returns variant with the given id */
108     @Nullable
getVariant(@onNull String id)109     public Variant getVariant(@NonNull String id) {
110         for (Variant variant : mVariants) {
111             if (variant.getId().equals(id)) {
112                 return variant;
113             }
114         }
115         return null;
116     }
117 
118     /** Sets variant with the given id */
setVariant(@onNull String id)119     public void setVariant(@NonNull String id) {
120         setVariant(id, null);
121     }
122 
123     /** Resets to the default variant */
resetVariant()124     public void resetVariant() {
125         setVariant(mDefaultVariant);
126     }
127 
128     /**
129      * Sets variant
130      *
131      * @param id The ID of the variant to set.
132      * @param event The event that triggered the variant change.
133      */
setVariant(@onNull String id, @Nullable Event event)134     public void setVariant(@NonNull String id, @Nullable Event event) {
135         for (Variant variant : mVariants) {
136             if (variant != null && variant.getId().equals(id)) {
137                 mCurrentVariant = variant;
138                 if (event != null) {
139                     mCurrentVariant.updateFromEvent(event);
140                 }
141                 return;
142             }
143         }
144     }
145 
146     /** Returns the role */
147     @NonNull
getRole()148     public Role getRole() {
149         return mRole;
150     }
151 
152     /** Returns true if animating */
isAnimating()153     public boolean isAnimating() {
154         return mRunningAnimator != null && mRunningAnimator.isRunning();
155     }
156 
157     /** Called on animation start */
onAnimationStart(@onNull Animator animator)158     public void onAnimationStart(@NonNull Animator animator) {
159         if (mRunningAnimator != null) {
160             mRunningAnimator.pause();
161             mRunningAnimator.removeAllListeners();
162         }
163         mRunningAnimator = animator;
164     }
165 
166     /** Called on animation end */
onAnimationEnd()167     public void onAnimationEnd() {
168         mRunningAnimator = null;
169     }
170 
171     /** Returns transition for the given event */
172     @Nullable
getTransition(@ullable Event event)173     public Transition getTransition(@Nullable Event event) {
174         if (event == null) {
175             return null;
176         }
177         // If both onEvent and fromVariant matches
178         String currentVariantId =
179                 (getCurrentVariant() != null) ? getCurrentVariant().getId() : null;
180         Transition result = getTransitionInternal(event, currentVariantId);
181 
182         if (result != null) {
183             return result;
184         }
185         // If only onEvent matches
186         return getTransitionInternal(event);
187     }
188 
189     @Nullable
getTransitionInternal(@onNull Event event, @Nullable String fromVariant)190     private Transition getTransitionInternal(@NonNull Event event, @Nullable String fromVariant) {
191         for (Transition transition : mTransitions) {
192             if (event.isMatch(transition.getOnEvent())
193                     && transition.getFromVariant() != null
194                     && transition.getFromVariant().getId().equals(fromVariant)) {
195                 return transition;
196             }
197         }
198         return null;
199     }
200 
201     @Nullable
getTransitionInternal(@onNull Event event)202     private Transition getTransitionInternal(@NonNull Event event) {
203         for (Transition transition : mTransitions) {
204             if (event.isMatch(transition.getOnEvent()) && transition.getFromVariant() == null) {
205                 return transition;
206             }
207         }
208         return null;
209     }
210 
211     /**
212      * Returns the ID of the display the panel is associated with.
213      *
214      * @return The display ID.
215      */
getDisplayId()216     public int getDisplayId() {
217         return mDisplayId;
218     }
219 
setDefaultVariant(@ullable String defaultVariant)220     void setDefaultVariant(@Nullable String defaultVariant) {
221         mDefaultVariant = defaultVariant;
222     }
223 
setDisplayId(int displayId)224     void setDisplayId(int displayId) {
225         mDisplayId = displayId;
226     }
227 
setVariants(@onNull List<Variant> variants)228     void setVariants(@NonNull List<Variant> variants) {
229         mVariants.clear();
230         mVariants.addAll(variants);
231     }
232 
setTransitions(@onNull List<Transition> transitions)233     void setTransitions(@NonNull List<Transition> transitions) {
234         mTransitions.clear();
235         mTransitions.addAll(transitions);
236     }
237 
238     @Override
239     @NonNull
toString()240     public String toString() {
241         return "PanelState{"
242                 + "mId='" + mId + '\''
243                 + ", mRole=" + mRole
244                 + ", mDefaultVariant='" + mDefaultVariant + '\''
245                 + ", mDisplayId=" + mDisplayId
246                 + ", mVariants=" + mVariants.stream()
247                     .map(Variant::toString)
248                     .collect(Collectors.joining(", ", "[", "]"))
249                 + ", mTransitions=" + mTransitions.stream()
250                     .map(Transition::toString)
251                     .collect(Collectors.joining(", ", "[", "]"))
252                 + ", mRunningAnimator=" + mRunningAnimator
253                 + ", mCurrentVariant="
254                 + (mCurrentVariant != null ? mCurrentVariant.getId() : "null")
255                 + '}';
256     }
257 
258     /** Builder for {@link PanelState} objects. */
259     public static class Builder {
260         private String mId;
261         private Role mRole;
262         private String mDefaultVariant;
263         private Integer mDisplayId;
264         private List<Variant> mVariants = new ArrayList<>();
265         private List<Transition> mTransitions = new ArrayList<>();
266 
Builder(@onNull String id, @NonNull Role role)267         public Builder(@NonNull String id, @NonNull Role role) {
268             mId = id;
269             mRole = role;
270         }
271 
272         /** Sets default variant */
setDefaultVariant(@ullable String defaultVariant)273         public Builder setDefaultVariant(@Nullable String defaultVariant) {
274             mDefaultVariant = defaultVariant;
275             return this;
276         }
277 
278         /** Sets display id */
setDisplayId(int displayId)279         public Builder setDisplayId(int displayId) {
280             mDisplayId = displayId;
281             return this;
282         }
283 
284         /** Adds a variant */
addVariant(@onNull Variant variant)285         public Builder addVariant(@NonNull Variant variant) {
286             mVariants.add(variant);
287             return this;
288         }
289 
290         /** Adds a transitions */
addTransition(@onNull Transition transition)291         public Builder addTransition(@NonNull Transition transition) {
292             mTransitions.add(transition);
293             return this;
294         }
295 
296         /** Sets variants */
setVariants(@onNull List<Variant> variants)297         public Builder setVariants(@NonNull List<Variant> variants) {
298             mVariants = new ArrayList<>(variants); // Defensive copy
299             return this;
300         }
301 
302         /** Sets transitions */
setTransitions(@onNull List<Transition> transitions)303         public Builder setTransitions(@NonNull List<Transition> transitions) {
304             mTransitions = new ArrayList<>(transitions); // Defensive copy
305             return this;
306         }
307 
308         /** Returns the {@link PanelState} instance */
309         @NonNull
build()310         public PanelState build() {
311             PanelState panelState = new PanelState(mId, mRole);
312             panelState.setDefaultVariant(mDefaultVariant);
313             if (mDisplayId != null) {
314                 panelState.setDisplayId(mDisplayId);
315             }
316             panelState.setVariants(mVariants);
317             panelState.setTransitions(mTransitions);
318             return panelState;
319         }
320     }
321 }
322