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