blob: abf29c47e803b4cd59e2f9eda7e9b20366b0dcae [file] [log] [blame]
Jason Monk1fd3fc32018-08-14 17:20:09 -04001/*
Beverly8fdb5332019-02-04 14:29:49 -05002 * Copyright (C) 2019 The Android Open Source Project
Jason Monk1fd3fc32018-08-14 17:20:09 -04003 *
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
Beverly8fdb5332019-02-04 14:29:49 -050014 * limitations under the License.
Jason Monk1fd3fc32018-08-14 17:20:09 -040015 */
16
17package com.android.systemui.statusbar;
18
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080019import android.animation.ObjectAnimator;
20import android.animation.ValueAnimator;
Evan Laird2ce144e2019-03-04 18:45:34 -050021import android.text.format.DateFormat;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080022import android.util.FloatProperty;
Lucas Dupine7ff7be2019-09-17 11:19:53 -040023import android.util.Log;
Beverlye14f08e2019-06-06 15:33:10 -040024import android.view.View;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080025import android.view.animation.Interpolator;
26
Evan Laird91d0f102018-09-18 17:39:55 -040027import com.android.internal.annotations.GuardedBy;
Evan Laird2ce144e2019-03-04 18:45:34 -050028import com.android.systemui.Dumpable;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080029import com.android.systemui.Interpolators;
Beverly8fdb5332019-02-04 14:29:49 -050030import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080031import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
Jason Monkaf08c152018-12-04 11:12:39 -050032import com.android.systemui.statusbar.policy.CallbackController;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080033
Evan Laird2ce144e2019-03-04 18:45:34 -050034import java.io.FileDescriptor;
35import java.io.PrintWriter;
Evan Laird91d0f102018-09-18 17:39:55 -040036import java.util.ArrayList;
37import java.util.Comparator;
Jason Monk1fd3fc32018-08-14 17:20:09 -040038
Jason Monk27d01a622018-12-10 15:57:09 -050039import javax.inject.Inject;
40import javax.inject.Singleton;
41
Jason Monk1fd3fc32018-08-14 17:20:09 -040042/**
43 * Tracks and reports on {@link StatusBarState}.
44 */
Jason Monk27d01a622018-12-10 15:57:09 -050045@Singleton
Beverly8fdb5332019-02-04 14:29:49 -050046public class StatusBarStateControllerImpl implements SysuiStatusBarStateController,
Evan Laird2ce144e2019-03-04 18:45:34 -050047 CallbackController<StateListener>, Dumpable {
Evan Laird91d0f102018-09-18 17:39:55 -040048 private static final String TAG = "SbStateController";
Evan Laird2ce144e2019-03-04 18:45:34 -050049 // Must be a power of 2
50 private static final int HISTORY_SIZE = 32;
Jason Monk1fd3fc32018-08-14 17:20:09 -040051
52 private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
53 private static final int MIN_STATE = StatusBarState.SHADE;
54
Beverly8fdb5332019-02-04 14:29:49 -050055 private static final Comparator<RankedListener> sComparator =
56 Comparator.comparingInt(o -> o.mRank);
57 private static final FloatProperty<StatusBarStateControllerImpl> SET_DARK_AMOUNT_PROPERTY =
58 new FloatProperty<StatusBarStateControllerImpl>("mDozeAmount") {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080059
60 @Override
Beverly8fdb5332019-02-04 14:29:49 -050061 public void setValue(StatusBarStateControllerImpl object, float value) {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080062 object.setDozeAmountInternal(value);
63 }
64
65 @Override
Beverly8fdb5332019-02-04 14:29:49 -050066 public Float get(StatusBarStateControllerImpl object) {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080067 return object.mDozeAmount;
68 }
69 };
Evan Laird91d0f102018-09-18 17:39:55 -040070
71 private final ArrayList<RankedListener> mListeners = new ArrayList<>();
Jason Monk1fd3fc32018-08-14 17:20:09 -040072 private int mState;
73 private int mLastState;
74 private boolean mLeaveOpenOnKeyguardHide;
Jason Monk297c04e2018-08-23 17:16:59 -040075 private boolean mKeyguardRequested;
Jason Monk1fd3fc32018-08-14 17:20:09 -040076
Evan Laird2ce144e2019-03-04 18:45:34 -050077 // Record the HISTORY_SIZE most recent states
78 private int mHistoryIndex = 0;
79 private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
80
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080081 /**
Beverlye14f08e2019-06-06 15:33:10 -040082 * Current SystemUiVisibility
83 */
84 private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
85
86 /**
87 * If the device is currently pulsing (AOD2).
88 */
89 private boolean mPulsing;
90
91 /**
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080092 * If the device is currently dozing or not.
93 */
94 private boolean mIsDozing;
95
96 /**
97 * Current {@link #mDozeAmount} animator.
98 */
99 private ValueAnimator mDarkAnimator;
100
101 /**
102 * Current doze amount in this frame.
103 */
104 private float mDozeAmount;
105
106 /**
107 * Where the animator will stop.
108 */
109 private float mDozeAmountTarget;
110
111 /**
112 * The type of interpolator that should be used to the doze animation.
113 */
114 private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
115
Jason Monk27d01a622018-12-10 15:57:09 -0500116 @Inject
Beverly8fdb5332019-02-04 14:29:49 -0500117 public StatusBarStateControllerImpl() {
Evan Laird2ce144e2019-03-04 18:45:34 -0500118 for (int i = 0; i < HISTORY_SIZE; i++) {
119 mHistoricalRecords[i] = new HistoricalState();
120 }
Jason Monk27d01a622018-12-10 15:57:09 -0500121 }
122
Beverly8fdb5332019-02-04 14:29:49 -0500123 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400124 public int getState() {
125 return mState;
126 }
127
Beverly8fdb5332019-02-04 14:29:49 -0500128 @Override
Evan Laird91d0f102018-09-18 17:39:55 -0400129 public boolean setState(int state) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400130 if (state > MAX_STATE || state < MIN_STATE) {
131 throw new IllegalArgumentException("Invalid state " + state);
132 }
133 if (state == mState) {
Evan Laird91d0f102018-09-18 17:39:55 -0400134 return false;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400135 }
Evan Laird2ce144e2019-03-04 18:45:34 -0500136
137 // Record the to-be mState and mLastState
138 recordHistoricalState(state, mState);
139
Lucas Dupine7ff7be2019-09-17 11:19:53 -0400140 // b/139259891
141 if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
142 Log.e(TAG, "Invalid state transition: SHADE -> SHADE_LOCKED", new Throwable());
143 }
144
Jason Monk1fd3fc32018-08-14 17:20:09 -0400145 synchronized (mListeners) {
Evan Laird91d0f102018-09-18 17:39:55 -0400146 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500147 rl.mListener.onStatePreChange(mState, state);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400148 }
149 mLastState = mState;
150 mState = state;
Evan Laird91d0f102018-09-18 17:39:55 -0400151 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500152 rl.mListener.onStateChanged(mState);
Evan Laird91d0f102018-09-18 17:39:55 -0400153 }
154
155 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500156 rl.mListener.onStatePostChange();
Jason Monk1fd3fc32018-08-14 17:20:09 -0400157 }
158 }
Evan Laird91d0f102018-09-18 17:39:55 -0400159
160 return true;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400161 }
162
Beverly8fdb5332019-02-04 14:29:49 -0500163 @Override
Evan Lairde84d8552018-10-15 15:08:45 -0400164 public boolean isDozing() {
165 return mIsDozing;
166 }
167
Beverly8fdb5332019-02-04 14:29:49 -0500168 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800169 public float getDozeAmount() {
170 return mDozeAmount;
171 }
172
Beverly8fdb5332019-02-04 14:29:49 -0500173 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800174 public float getInterpolatedDozeAmount() {
175 return mDozeInterpolator.getInterpolation(mDozeAmount);
176 }
177
Beverly8fdb5332019-02-04 14:29:49 -0500178 @Override
Evan Lairde84d8552018-10-15 15:08:45 -0400179 public boolean setIsDozing(boolean isDozing) {
180 if (mIsDozing == isDozing) {
181 return false;
182 }
183
184 mIsDozing = isDozing;
185
186 synchronized (mListeners) {
187 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500188 rl.mListener.onDozingChanged(isDozing);
Evan Lairde84d8552018-10-15 15:08:45 -0400189 }
190 }
191
192 return true;
193 }
194
Beverly8fdb5332019-02-04 14:29:49 -0500195 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800196 public void setDozeAmount(float dozeAmount, boolean animated) {
197 if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
198 if (animated && mDozeAmountTarget == dozeAmount) {
199 return;
200 } else {
201 mDarkAnimator.cancel();
202 }
203 }
204
205 mDozeAmountTarget = dozeAmount;
206 if (animated) {
207 startDozeAnimation();
208 } else {
209 setDozeAmountInternal(dozeAmount);
210 }
211 }
212
213 private void startDozeAnimation() {
214 if (mDozeAmount == 0f || mDozeAmount == 1f) {
215 mDozeInterpolator = mIsDozing
216 ? Interpolators.FAST_OUT_SLOW_IN
217 : Interpolators.TOUCH_RESPONSE_REVERSE;
218 }
219 mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget);
220 mDarkAnimator.setInterpolator(Interpolators.LINEAR);
221 mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
222 mDarkAnimator.start();
223 }
224
225 private void setDozeAmountInternal(float dozeAmount) {
226 mDozeAmount = dozeAmount;
227 float interpolatedAmount = mDozeInterpolator.getInterpolation(dozeAmount);
228 synchronized (mListeners) {
229 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500230 rl.mListener.onDozeAmountChanged(mDozeAmount, interpolatedAmount);
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800231 }
232 }
233 }
234
Beverly8fdb5332019-02-04 14:29:49 -0500235 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400236 public boolean goingToFullShade() {
237 return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
238 }
239
Beverly8fdb5332019-02-04 14:29:49 -0500240 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400241 public void setLeaveOpenOnKeyguardHide(boolean leaveOpen) {
242 mLeaveOpenOnKeyguardHide = leaveOpen;
243 }
244
Beverly8fdb5332019-02-04 14:29:49 -0500245 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400246 public boolean leaveOpenOnKeyguardHide() {
247 return mLeaveOpenOnKeyguardHide;
248 }
249
Beverly8fdb5332019-02-04 14:29:49 -0500250 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400251 public boolean fromShadeLocked() {
252 return mLastState == StatusBarState.SHADE_LOCKED;
253 }
254
Beverly8fdb5332019-02-04 14:29:49 -0500255 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500256 public void addCallback(StateListener listener) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400257 synchronized (mListeners) {
Evan Laird91d0f102018-09-18 17:39:55 -0400258 addListenerInternalLocked(listener, Integer.MAX_VALUE);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400259 }
260 }
261
Evan Laird91d0f102018-09-18 17:39:55 -0400262 /**
263 * Add a listener and a rank based on the priority of this message
264 * @param listener the listener
265 * @param rank the order in which you'd like to be called. Ranked listeners will be
266 * notified before unranked, and we will sort ranked listeners from low to high
267 *
268 * @deprecated This method exists only to solve latent inter-dependencies from refactoring
269 * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
270 * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
271 */
Beverly8fdb5332019-02-04 14:29:49 -0500272 @Deprecated
273 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500274 public void addCallback(StateListener listener, @SbStateListenerRank int rank) {
Evan Laird91d0f102018-09-18 17:39:55 -0400275 synchronized (mListeners) {
276 addListenerInternalLocked(listener, rank);
277 }
278 }
279
280 @GuardedBy("mListeners")
281 private void addListenerInternalLocked(StateListener listener, int rank) {
282 // Protect against double-subscribe
283 for (RankedListener rl : mListeners) {
Beverly8fdb5332019-02-04 14:29:49 -0500284 if (rl.mListener.equals(listener)) {
Evan Laird91d0f102018-09-18 17:39:55 -0400285 return;
286 }
287 }
288
Beverly8fdb5332019-02-04 14:29:49 -0500289 RankedListener rl = new SysuiStatusBarStateController.RankedListener(listener, rank);
Evan Laird91d0f102018-09-18 17:39:55 -0400290 mListeners.add(rl);
Beverly8fdb5332019-02-04 14:29:49 -0500291 mListeners.sort(sComparator);
Evan Laird91d0f102018-09-18 17:39:55 -0400292 }
293
Beverly8fdb5332019-02-04 14:29:49 -0500294
295 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500296 public void removeCallback(StateListener listener) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400297 synchronized (mListeners) {
Beverly8fdb5332019-02-04 14:29:49 -0500298 mListeners.removeIf((it) -> it.mListener.equals(listener));
Jason Monk1fd3fc32018-08-14 17:20:09 -0400299 }
300 }
301
Beverly8fdb5332019-02-04 14:29:49 -0500302 @Override
Jason Monk297c04e2018-08-23 17:16:59 -0400303 public void setKeyguardRequested(boolean keyguardRequested) {
304 mKeyguardRequested = keyguardRequested;
305 }
306
Beverly8fdb5332019-02-04 14:29:49 -0500307 @Override
Jason Monk297c04e2018-08-23 17:16:59 -0400308 public boolean isKeyguardRequested() {
309 return mKeyguardRequested;
310 }
311
Beverlye14f08e2019-06-06 15:33:10 -0400312 @Override
313 public void setSystemUiVisibility(int visibility) {
314 if (mSystemUiVisibility != visibility) {
315 mSystemUiVisibility = visibility;
316 synchronized (mListeners) {
317 for (RankedListener rl : new ArrayList<>(mListeners)) {
318 rl.mListener.onSystemUiVisibilityChanged(mSystemUiVisibility);
319 }
320 }
321 }
322 }
323
324 @Override
325 public void setPulsing(boolean pulsing) {
326 if (mPulsing != pulsing) {
327 mPulsing = pulsing;
328 synchronized (mListeners) {
329 for (RankedListener rl : new ArrayList<>(mListeners)) {
330 rl.mListener.onPulsingChanged(pulsing);
331 }
332 }
333 }
334 }
335
Beverly8fdb5332019-02-04 14:29:49 -0500336 /**
337 * Returns String readable state of status bar from {@link StatusBarState}
338 */
Evan Laird91d0f102018-09-18 17:39:55 -0400339 public static String describe(int state) {
340 return StatusBarState.toShortString(state);
341 }
Evan Laird2ce144e2019-03-04 18:45:34 -0500342
343 @Override
344 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
345 pw.println("StatusBarStateController: ");
346 pw.println(" mState=" + mState + " (" + describe(mState) + ")");
347 pw.println(" mLastState=" + mLastState + " (" + describe(mLastState) + ")");
348 pw.println(" mLeaveOpenOnKeyguardHide=" + mLeaveOpenOnKeyguardHide);
349 pw.println(" mKeyguardRequested=" + mKeyguardRequested);
350 pw.println(" mIsDozing=" + mIsDozing);
351 pw.println(" Historical states:");
352 // Ignore records without a timestamp
353 int size = 0;
354 for (int i = 0; i < HISTORY_SIZE; i++) {
355 if (mHistoricalRecords[i].mTimestamp != 0) size++;
356 }
357 for (int i = mHistoryIndex + HISTORY_SIZE;
358 i >= mHistoryIndex + HISTORY_SIZE - size + 1; i--) {
359 pw.println(" (" + (mHistoryIndex + HISTORY_SIZE - i + 1) + ")"
360 + mHistoricalRecords[i & (HISTORY_SIZE - 1)]);
361 }
362 }
363
364 private void recordHistoricalState(int currentState, int lastState) {
365 mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
366 HistoricalState state = mHistoricalRecords[mHistoryIndex];
367 state.mState = currentState;
368 state.mLastState = lastState;
369 state.mTimestamp = System.currentTimeMillis();
370 }
371
372 /**
373 * For keeping track of our previous state to help with debugging
374 */
375 private static class HistoricalState {
376 int mState;
377 int mLastState;
378 long mTimestamp;
379
380 @Override
381 public String toString() {
382 if (mTimestamp != 0) {
383 StringBuilder sb = new StringBuilder();
384 sb.append("state=").append(mState)
385 .append(" (").append(describe(mState)).append(")");
386 sb.append("lastState=").append(mLastState).append(" (").append(describe(mLastState))
387 .append(")");
388 sb.append("timestamp=")
389 .append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp));
390
391 return sb.toString();
392 }
393 return "Empty " + getClass().getSimpleName();
394 }
395 }
Jason Monk1fd3fc32018-08-14 17:20:09 -0400396}