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