• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.systemui.statusbar.phone;
18 
19 import android.animation.ValueAnimator;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.os.SystemClock;
24 import android.util.MathUtils;
25 import android.util.TimeUtils;
26 
27 import androidx.annotation.VisibleForTesting;
28 
29 import com.android.app.animation.Interpolators;
30 import com.android.internal.policy.GestureNavigationSettingsObserver;
31 import com.android.systemui.Dumpable;
32 import com.android.systemui.dagger.qualifiers.Background;
33 import com.android.systemui.plugins.statusbar.StatusBarStateController;
34 import com.android.systemui.shared.system.QuickStepContract;
35 import com.android.systemui.statusbar.CommandQueue;
36 import com.android.systemui.statusbar.CommandQueue.Callbacks;
37 import com.android.systemui.statusbar.policy.KeyguardStateController;
38 
39 import dagger.assisted.Assisted;
40 import dagger.assisted.AssistedFactory;
41 import dagger.assisted.AssistedInject;
42 
43 import java.io.PrintWriter;
44 import java.lang.ref.WeakReference;
45 
46 /**
47  * Class to control all aspects about light bar changes.
48  */
49 public class LightBarTransitionsController implements Dumpable {
50 
51     public static final int DEFAULT_TINT_ANIMATION_DURATION = 120;
52     private static final String EXTRA_DARK_INTENSITY = "dark_intensity";
53 
54     private static class Callback implements Callbacks, StatusBarStateController.StateListener {
55         private final WeakReference<LightBarTransitionsController> mSelf;
56 
Callback(LightBarTransitionsController self)57         Callback(LightBarTransitionsController self) {
58             mSelf = new WeakReference<>(self);
59         }
60 
61         @Override
appTransitionPending(int displayId, boolean forced)62         public void appTransitionPending(int displayId, boolean forced) {
63             LightBarTransitionsController self = mSelf.get();
64             if (self != null) {
65                 self.appTransitionPending(displayId, forced);
66             }
67         }
68 
69         @Override
appTransitionCancelled(int displayId)70         public void appTransitionCancelled(int displayId) {
71             LightBarTransitionsController self = mSelf.get();
72             if (self != null) {
73                 self.appTransitionCancelled(displayId);
74             }
75         }
76 
77         @Override
appTransitionStarting(int displayId, long startTime, long duration, boolean forced)78         public void appTransitionStarting(int displayId, long startTime, long duration,
79                 boolean forced) {
80             LightBarTransitionsController self = mSelf.get();
81             if (self != null) {
82                 self.appTransitionStarting(displayId, startTime, duration, forced);
83             }
84         }
85 
86         @Override
onDozeAmountChanged(float linear, float eased)87         public void onDozeAmountChanged(float linear, float eased) {
88             LightBarTransitionsController self = mSelf.get();
89             if (self != null) {
90                 self.onDozeAmountChanged(linear, eased);
91             }
92         }
93     }
94 
95     private final Callback mCallback;
96 
97     private final Handler mHandler;
98     private final DarkIntensityApplier mApplier;
99     private final KeyguardStateController mKeyguardStateController;
100     private final StatusBarStateController mStatusBarStateController;
101     private final CommandQueue mCommandQueue;
102     private GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
103 
104     private boolean mTransitionDeferring;
105     private long mTransitionDeferringStartTime;
106     private long mTransitionDeferringDuration;
107     private boolean mTransitionPending;
108     private boolean mTintChangePending;
109     private boolean mNavigationButtonsForcedVisible;
110     private float mPendingDarkIntensity;
111     private ValueAnimator mTintAnimator;
112     private float mDarkIntensity;
113     private float mNextDarkIntensity;
114     private float mDozeAmount;
115     private int mDisplayId;
116     private final Runnable mTransitionDeferringDoneRunnable = new Runnable() {
117         @Override
118         public void run() {
119             mTransitionDeferring = false;
120         }
121     };
122 
123     private final Context mContext;
124 
125     @AssistedInject
LightBarTransitionsController( Context context, @Background Handler bgHandler, @Assisted DarkIntensityApplier applier, CommandQueue commandQueue, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController)126     public LightBarTransitionsController(
127             Context context,
128             @Background Handler bgHandler,
129             @Assisted DarkIntensityApplier applier,
130             CommandQueue commandQueue,
131             KeyguardStateController keyguardStateController,
132             StatusBarStateController statusBarStateController) {
133         mApplier = applier;
134         mHandler = new Handler();
135         mKeyguardStateController = keyguardStateController;
136         mStatusBarStateController = statusBarStateController;
137         mCommandQueue = commandQueue;
138         mCallback = new Callback(this);
139         mCommandQueue.addCallback(mCallback);
140         mStatusBarStateController.addCallback(mCallback);
141         mDozeAmount = mStatusBarStateController.getDozeAmount();
142         mContext = context;
143         mDisplayId = mContext.getDisplayId();
144         mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
145                 mHandler, bgHandler, mContext, this::onNavigationSettingsChanged);
146         mGestureNavigationSettingsObserver.register();
147         onNavigationSettingsChanged();
148     }
149 
150     /** Call to cleanup the LightBarTransitionsController when done with it. */
destroy()151     public void destroy() {
152         mCommandQueue.removeCallback(mCallback);
153         mStatusBarStateController.removeCallback(mCallback);
154         mGestureNavigationSettingsObserver.unregister();
155     }
156 
saveState(Bundle outState)157     public void saveState(Bundle outState) {
158         float intensity = mTintAnimator != null && mTintAnimator.isRunning()
159                 ?  mNextDarkIntensity : mDarkIntensity;
160         outState.putFloat(EXTRA_DARK_INTENSITY, intensity);
161     }
162 
restoreState(Bundle savedInstanceState)163     public void restoreState(Bundle savedInstanceState) {
164         setIconTintInternal(savedInstanceState.getFloat(EXTRA_DARK_INTENSITY, 0));
165         mNextDarkIntensity = mDarkIntensity;
166     }
167 
appTransitionPending(int displayId, boolean forced)168     private void appTransitionPending(int displayId, boolean forced) {
169         if (mDisplayId != displayId || mKeyguardStateController.isKeyguardGoingAway() && !forced) {
170             return;
171         }
172         mTransitionPending = true;
173     }
174 
appTransitionCancelled(int displayId)175     private void appTransitionCancelled(int displayId) {
176         if (mDisplayId != displayId) {
177             return;
178         }
179         if (mTransitionPending && mTintChangePending) {
180             mTintChangePending = false;
181             animateIconTint(mPendingDarkIntensity, 0 /* delay */,
182                     mApplier.getTintAnimationDuration());
183         }
184         mTransitionPending = false;
185     }
186 
appTransitionStarting(int displayId, long startTime, long duration, boolean forced)187     private void appTransitionStarting(int displayId, long startTime, long duration, boolean forced) {
188         if (mDisplayId != displayId || mKeyguardStateController.isKeyguardGoingAway() && !forced) {
189             return;
190         }
191         if (mTransitionPending && mTintChangePending) {
192             mTintChangePending = false;
193             animateIconTint(mPendingDarkIntensity,
194                     Math.max(0, startTime - SystemClock.uptimeMillis()),
195                     duration);
196 
197         } else if (mTransitionPending) {
198 
199             // If we don't have a pending tint change yet, the change might come in the future until
200             // startTime is reached.
201             mTransitionDeferring = true;
202             mTransitionDeferringStartTime = startTime;
203             mTransitionDeferringDuration = duration;
204             mHandler.removeCallbacks(mTransitionDeferringDoneRunnable);
205             mHandler.postAtTime(mTransitionDeferringDoneRunnable, startTime);
206         }
207         mTransitionPending = false;
208     }
209 
210     @VisibleForTesting
setNavigationSettingsObserver(GestureNavigationSettingsObserver observer)211     void setNavigationSettingsObserver(GestureNavigationSettingsObserver observer) {
212         mGestureNavigationSettingsObserver = observer;
213         onNavigationSettingsChanged();
214     }
215 
setIconsDark(boolean dark, boolean animate)216     public void setIconsDark(boolean dark, boolean animate) {
217         if (!animate) {
218             setIconTintInternal(dark ? 1.0f : 0.0f);
219             mNextDarkIntensity = dark ? 1.0f : 0.0f;
220         } else if (mTransitionPending) {
221             deferIconTintChange(dark ? 1.0f : 0.0f);
222         } else if (mTransitionDeferring) {
223             animateIconTint(dark ? 1.0f : 0.0f,
224                     Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
225                     mTransitionDeferringDuration);
226         } else {
227             animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, mApplier.getTintAnimationDuration());
228         }
229     }
230 
getCurrentDarkIntensity()231     public float getCurrentDarkIntensity() {
232         return mDarkIntensity;
233     }
234 
deferIconTintChange(float darkIntensity)235     private void deferIconTintChange(float darkIntensity) {
236         if (mTintChangePending && darkIntensity == mPendingDarkIntensity) {
237             return;
238         }
239         mTintChangePending = true;
240         mPendingDarkIntensity = darkIntensity;
241     }
242 
animateIconTint(float targetDarkIntensity, long delay, long duration)243     private void animateIconTint(float targetDarkIntensity, long delay,
244             long duration) {
245         if (mNextDarkIntensity == targetDarkIntensity) {
246             return;
247         }
248         if (mTintAnimator != null) {
249             mTintAnimator.cancel();
250         }
251         mNextDarkIntensity = targetDarkIntensity;
252         mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity);
253         mTintAnimator.addUpdateListener(
254                 animation -> setIconTintInternal((Float) animation.getAnimatedValue()));
255         mTintAnimator.setDuration(duration);
256         mTintAnimator.setStartDelay(delay);
257         mTintAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
258         mTintAnimator.start();
259     }
260 
setIconTintInternal(float darkIntensity)261     private void setIconTintInternal(float darkIntensity) {
262         mDarkIntensity = darkIntensity;
263         dispatchDark();
264     }
265 
dispatchDark()266     private void dispatchDark() {
267         mApplier.applyDarkIntensity(MathUtils.lerp(mDarkIntensity, 0f, mDozeAmount));
268     }
269 
onDozeAmountChanged(float linear, float eased)270     public void onDozeAmountChanged(float linear, float eased) {
271         mDozeAmount = eased;
272         dispatchDark();
273     }
274 
275     /**
276      * Called when the navigation settings change.
277      */
onNavigationSettingsChanged()278     private void onNavigationSettingsChanged() {
279         mNavigationButtonsForcedVisible =
280                 mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
281     }
282 
283     /**
284      * Return whether to use the tint calculated in this class for nav icons.
285      */
supportsIconTintForNavMode(int navigationMode)286     public boolean supportsIconTintForNavMode(int navigationMode) {
287         // In gesture mode, we already do region sampling to update tint based on content beneath.
288         return !QuickStepContract.isGesturalMode(navigationMode)
289                 || mNavigationButtonsForcedVisible;
290     }
291 
292     @Override
dump(PrintWriter pw, String[] args)293     public void dump(PrintWriter pw, String[] args) {
294         pw.print("  mTransitionDeferring="); pw.print(mTransitionDeferring);
295         if (mTransitionDeferring) {
296             pw.println();
297             pw.print("   mTransitionDeferringStartTime=");
298             pw.println(TimeUtils.formatUptime(mTransitionDeferringStartTime));
299 
300             pw.print("   mTransitionDeferringDuration=");
301             TimeUtils.formatDuration(mTransitionDeferringDuration, pw);
302             pw.println();
303         }
304         pw.print("  mTransitionPending="); pw.print(mTransitionPending);
305         pw.print(" mTintChangePending="); pw.println(mTintChangePending);
306 
307         pw.print("  mPendingDarkIntensity="); pw.print(mPendingDarkIntensity);
308         pw.print(" mDarkIntensity="); pw.print(mDarkIntensity);
309         pw.print(" mNextDarkIntensity="); pw.println(mNextDarkIntensity);
310         pw.print(" mAreNavigationButtonForcedVisible="); pw.println(mNavigationButtonsForcedVisible);
311     }
312 
313     /**
314      * Interface to apply a specific dark intensity.
315      */
316     public interface DarkIntensityApplier {
applyDarkIntensity(float darkIntensity)317         void applyDarkIntensity(float darkIntensity);
getTintAnimationDuration()318         int getTintAnimationDuration();
319     }
320 
321     /** Injectable factory for construction a LightBarTransitionsController. */
322     @AssistedFactory
323     public interface Factory {
324         /** */
create(DarkIntensityApplier darkIntensityApplier)325         LightBarTransitionsController create(DarkIntensityApplier darkIntensityApplier);
326     }
327 }
328