blob: 2bef355d59f31d09a5b3ba9f1401df00b8556618 [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;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080024import android.view.animation.Interpolator;
25
Evan Laird91d0f102018-09-18 17:39:55 -040026import com.android.internal.annotations.GuardedBy;
Fabian Kozynski2ff6df92020-04-24 12:00:49 -040027import com.android.internal.logging.UiEventLogger;
Lucas Dupin8968f6a2019-08-09 17:41:15 -070028import com.android.systemui.DejankUtils;
Evan Laird2ce144e2019-03-04 18:45:34 -050029import com.android.systemui.Dumpable;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080030import com.android.systemui.Interpolators;
Beverly8fdb5332019-02-04 14:29:49 -050031import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080032import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
Jason Monkaf08c152018-12-04 11:12:39 -050033import com.android.systemui.statusbar.policy.CallbackController;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080034
Evan Laird2ce144e2019-03-04 18:45:34 -050035import java.io.FileDescriptor;
36import java.io.PrintWriter;
Evan Laird91d0f102018-09-18 17:39:55 -040037import java.util.ArrayList;
38import java.util.Comparator;
Jason Monk1fd3fc32018-08-14 17:20:09 -040039
Jason Monk27d01a622018-12-10 15:57:09 -050040import javax.inject.Inject;
41import javax.inject.Singleton;
42
Jason Monk1fd3fc32018-08-14 17:20:09 -040043/**
44 * Tracks and reports on {@link StatusBarState}.
45 */
Jason Monk27d01a622018-12-10 15:57:09 -050046@Singleton
Beverly8fdb5332019-02-04 14:29:49 -050047public class StatusBarStateControllerImpl implements SysuiStatusBarStateController,
Evan Laird2ce144e2019-03-04 18:45:34 -050048 CallbackController<StateListener>, Dumpable {
Evan Laird91d0f102018-09-18 17:39:55 -040049 private static final String TAG = "SbStateController";
Evan Laird2ce144e2019-03-04 18:45:34 -050050 // Must be a power of 2
51 private static final int HISTORY_SIZE = 32;
Jason Monk1fd3fc32018-08-14 17:20:09 -040052
53 private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
54 private static final int MIN_STATE = StatusBarState.SHADE;
55
Beverly8fdb5332019-02-04 14:29:49 -050056 private static final Comparator<RankedListener> sComparator =
57 Comparator.comparingInt(o -> o.mRank);
58 private static final FloatProperty<StatusBarStateControllerImpl> SET_DARK_AMOUNT_PROPERTY =
59 new FloatProperty<StatusBarStateControllerImpl>("mDozeAmount") {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080060
61 @Override
Beverly8fdb5332019-02-04 14:29:49 -050062 public void setValue(StatusBarStateControllerImpl object, float value) {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080063 object.setDozeAmountInternal(value);
64 }
65
66 @Override
Beverly8fdb5332019-02-04 14:29:49 -050067 public Float get(StatusBarStateControllerImpl object) {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080068 return object.mDozeAmount;
69 }
70 };
Evan Laird91d0f102018-09-18 17:39:55 -040071
72 private final ArrayList<RankedListener> mListeners = new ArrayList<>();
Fabian Kozynski2ff6df92020-04-24 12:00:49 -040073 private final UiEventLogger mUiEventLogger;
Jason Monk1fd3fc32018-08-14 17:20:09 -040074 private int mState;
75 private int mLastState;
76 private boolean mLeaveOpenOnKeyguardHide;
Jason Monk297c04e2018-08-23 17:16:59 -040077 private boolean mKeyguardRequested;
Jason Monk1fd3fc32018-08-14 17:20:09 -040078
Evan Laird2ce144e2019-03-04 18:45:34 -050079 // Record the HISTORY_SIZE most recent states
80 private int mHistoryIndex = 0;
81 private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
82
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080083 /**
Jorim Jaggi956ca412019-01-07 14:49:14 +010084 * If any of the system bars is hidden.
Beverlye14f08e2019-06-06 15:33:10 -040085 */
Jorim Jaggi956ca412019-01-07 14:49:14 +010086 private boolean mIsFullscreen = false;
87
88 /**
89 * If the navigation bar can stay hidden when the display gets tapped.
90 */
91 private boolean mIsImmersive = false;
Beverlye14f08e2019-06-06 15:33:10 -040092
93 /**
94 * If the device is currently pulsing (AOD2).
95 */
96 private boolean mPulsing;
97
98 /**
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080099 * If the device is currently dozing or not.
100 */
101 private boolean mIsDozing;
102
103 /**
104 * Current {@link #mDozeAmount} animator.
105 */
106 private ValueAnimator mDarkAnimator;
107
108 /**
109 * Current doze amount in this frame.
110 */
111 private float mDozeAmount;
112
113 /**
114 * Where the animator will stop.
115 */
116 private float mDozeAmountTarget;
117
118 /**
119 * The type of interpolator that should be used to the doze animation.
120 */
121 private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
122
Jason Monk27d01a622018-12-10 15:57:09 -0500123 @Inject
Fabian Kozynski2ff6df92020-04-24 12:00:49 -0400124 public StatusBarStateControllerImpl(UiEventLogger uiEventLogger) {
125 mUiEventLogger = uiEventLogger;
Evan Laird2ce144e2019-03-04 18:45:34 -0500126 for (int i = 0; i < HISTORY_SIZE; i++) {
127 mHistoricalRecords[i] = new HistoricalState();
128 }
Jason Monk27d01a622018-12-10 15:57:09 -0500129 }
130
Beverly8fdb5332019-02-04 14:29:49 -0500131 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400132 public int getState() {
133 return mState;
134 }
135
Beverly8fdb5332019-02-04 14:29:49 -0500136 @Override
Evan Laird91d0f102018-09-18 17:39:55 -0400137 public boolean setState(int state) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400138 if (state > MAX_STATE || state < MIN_STATE) {
139 throw new IllegalArgumentException("Invalid state " + state);
140 }
141 if (state == mState) {
Evan Laird91d0f102018-09-18 17:39:55 -0400142 return false;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400143 }
Evan Laird2ce144e2019-03-04 18:45:34 -0500144
145 // Record the to-be mState and mLastState
146 recordHistoricalState(state, mState);
147
Lucas Dupine7ff7be2019-09-17 11:19:53 -0400148 // b/139259891
149 if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
150 Log.e(TAG, "Invalid state transition: SHADE -> SHADE_LOCKED", new Throwable());
151 }
152
Jason Monk1fd3fc32018-08-14 17:20:09 -0400153 synchronized (mListeners) {
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700154 String tag = getClass().getSimpleName() + "#setState(" + state + ")";
155 DejankUtils.startDetectingBlockingIpcs(tag);
Evan Laird91d0f102018-09-18 17:39:55 -0400156 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500157 rl.mListener.onStatePreChange(mState, state);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400158 }
159 mLastState = mState;
160 mState = state;
Fabian Kozynski2ff6df92020-04-24 12:00:49 -0400161 mUiEventLogger.log(StatusBarStateEvent.fromState(mState));
Evan Laird91d0f102018-09-18 17:39:55 -0400162 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500163 rl.mListener.onStateChanged(mState);
Evan Laird91d0f102018-09-18 17:39:55 -0400164 }
165
166 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500167 rl.mListener.onStatePostChange();
Jason Monk1fd3fc32018-08-14 17:20:09 -0400168 }
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700169 DejankUtils.stopDetectingBlockingIpcs(tag);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400170 }
Evan Laird91d0f102018-09-18 17:39:55 -0400171
172 return true;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400173 }
174
Beverly8fdb5332019-02-04 14:29:49 -0500175 @Override
Evan Lairde84d8552018-10-15 15:08:45 -0400176 public boolean isDozing() {
177 return mIsDozing;
178 }
179
Beverly8fdb5332019-02-04 14:29:49 -0500180 @Override
Dave Mankoff50883d72020-05-01 16:26:40 -0400181 public boolean isPulsing() {
182 return mPulsing;
183 }
184
185 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800186 public float getDozeAmount() {
187 return mDozeAmount;
188 }
189
Beverly8fdb5332019-02-04 14:29:49 -0500190 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800191 public float getInterpolatedDozeAmount() {
192 return mDozeInterpolator.getInterpolation(mDozeAmount);
193 }
194
Beverly8fdb5332019-02-04 14:29:49 -0500195 @Override
Evan Lairde84d8552018-10-15 15:08:45 -0400196 public boolean setIsDozing(boolean isDozing) {
197 if (mIsDozing == isDozing) {
198 return false;
199 }
200
201 mIsDozing = isDozing;
202
203 synchronized (mListeners) {
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700204 String tag = getClass().getSimpleName() + "#setIsDozing";
205 DejankUtils.startDetectingBlockingIpcs(tag);
Evan Lairde84d8552018-10-15 15:08:45 -0400206 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500207 rl.mListener.onDozingChanged(isDozing);
Evan Lairde84d8552018-10-15 15:08:45 -0400208 }
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700209 DejankUtils.stopDetectingBlockingIpcs(tag);
Evan Lairde84d8552018-10-15 15:08:45 -0400210 }
211
212 return true;
213 }
214
Beverly8fdb5332019-02-04 14:29:49 -0500215 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800216 public void setDozeAmount(float dozeAmount, boolean animated) {
217 if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
218 if (animated && mDozeAmountTarget == dozeAmount) {
219 return;
220 } else {
221 mDarkAnimator.cancel();
222 }
223 }
224
225 mDozeAmountTarget = dozeAmount;
226 if (animated) {
227 startDozeAnimation();
228 } else {
229 setDozeAmountInternal(dozeAmount);
230 }
231 }
232
233 private void startDozeAnimation() {
234 if (mDozeAmount == 0f || mDozeAmount == 1f) {
235 mDozeInterpolator = mIsDozing
236 ? Interpolators.FAST_OUT_SLOW_IN
237 : Interpolators.TOUCH_RESPONSE_REVERSE;
238 }
239 mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget);
240 mDarkAnimator.setInterpolator(Interpolators.LINEAR);
241 mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
242 mDarkAnimator.start();
243 }
244
245 private void setDozeAmountInternal(float dozeAmount) {
246 mDozeAmount = dozeAmount;
247 float interpolatedAmount = mDozeInterpolator.getInterpolation(dozeAmount);
248 synchronized (mListeners) {
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700249 String tag = getClass().getSimpleName() + "#setDozeAmount";
250 DejankUtils.startDetectingBlockingIpcs(tag);
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800251 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500252 rl.mListener.onDozeAmountChanged(mDozeAmount, interpolatedAmount);
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800253 }
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700254 DejankUtils.stopDetectingBlockingIpcs(tag);
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800255 }
256 }
257
Beverly8fdb5332019-02-04 14:29:49 -0500258 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400259 public boolean goingToFullShade() {
260 return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
261 }
262
Beverly8fdb5332019-02-04 14:29:49 -0500263 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400264 public void setLeaveOpenOnKeyguardHide(boolean leaveOpen) {
265 mLeaveOpenOnKeyguardHide = leaveOpen;
266 }
267
Beverly8fdb5332019-02-04 14:29:49 -0500268 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400269 public boolean leaveOpenOnKeyguardHide() {
270 return mLeaveOpenOnKeyguardHide;
271 }
272
Beverly8fdb5332019-02-04 14:29:49 -0500273 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400274 public boolean fromShadeLocked() {
275 return mLastState == StatusBarState.SHADE_LOCKED;
276 }
277
Beverly8fdb5332019-02-04 14:29:49 -0500278 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500279 public void addCallback(StateListener listener) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400280 synchronized (mListeners) {
Evan Laird91d0f102018-09-18 17:39:55 -0400281 addListenerInternalLocked(listener, Integer.MAX_VALUE);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400282 }
283 }
284
Evan Laird91d0f102018-09-18 17:39:55 -0400285 /**
286 * Add a listener and a rank based on the priority of this message
287 * @param listener the listener
288 * @param rank the order in which you'd like to be called. Ranked listeners will be
289 * notified before unranked, and we will sort ranked listeners from low to high
290 *
291 * @deprecated This method exists only to solve latent inter-dependencies from refactoring
292 * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
293 * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
294 */
Beverly8fdb5332019-02-04 14:29:49 -0500295 @Deprecated
296 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500297 public void addCallback(StateListener listener, @SbStateListenerRank int rank) {
Evan Laird91d0f102018-09-18 17:39:55 -0400298 synchronized (mListeners) {
299 addListenerInternalLocked(listener, rank);
300 }
301 }
302
303 @GuardedBy("mListeners")
304 private void addListenerInternalLocked(StateListener listener, int rank) {
305 // Protect against double-subscribe
306 for (RankedListener rl : mListeners) {
Beverly8fdb5332019-02-04 14:29:49 -0500307 if (rl.mListener.equals(listener)) {
Evan Laird91d0f102018-09-18 17:39:55 -0400308 return;
309 }
310 }
311
Beverly8fdb5332019-02-04 14:29:49 -0500312 RankedListener rl = new SysuiStatusBarStateController.RankedListener(listener, rank);
Evan Laird91d0f102018-09-18 17:39:55 -0400313 mListeners.add(rl);
Beverly8fdb5332019-02-04 14:29:49 -0500314 mListeners.sort(sComparator);
Evan Laird91d0f102018-09-18 17:39:55 -0400315 }
316
Beverly8fdb5332019-02-04 14:29:49 -0500317
318 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500319 public void removeCallback(StateListener listener) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400320 synchronized (mListeners) {
Beverly8fdb5332019-02-04 14:29:49 -0500321 mListeners.removeIf((it) -> it.mListener.equals(listener));
Jason Monk1fd3fc32018-08-14 17:20:09 -0400322 }
323 }
324
Beverly8fdb5332019-02-04 14:29:49 -0500325 @Override
Jason Monk297c04e2018-08-23 17:16:59 -0400326 public void setKeyguardRequested(boolean keyguardRequested) {
327 mKeyguardRequested = keyguardRequested;
328 }
329
Beverly8fdb5332019-02-04 14:29:49 -0500330 @Override
Jason Monk297c04e2018-08-23 17:16:59 -0400331 public boolean isKeyguardRequested() {
332 return mKeyguardRequested;
333 }
334
Beverlye14f08e2019-06-06 15:33:10 -0400335 @Override
Jorim Jaggi956ca412019-01-07 14:49:14 +0100336 public void setFullscreenState(boolean isFullscreen, boolean isImmersive) {
337 if (mIsFullscreen != isFullscreen || mIsImmersive != isImmersive) {
338 mIsFullscreen = isFullscreen;
339 mIsImmersive = isImmersive;
Ioannis Ilkos8cca1412019-10-17 09:38:00 +0000340 synchronized (mListeners) {
341 for (RankedListener rl : new ArrayList<>(mListeners)) {
Jorim Jaggi956ca412019-01-07 14:49:14 +0100342 rl.mListener.onFullscreenStateChanged(isFullscreen, isImmersive);
Ioannis Ilkos8cca1412019-10-17 09:38:00 +0000343 }
344 }
345 }
346 }
347
348 @Override
Beverlye14f08e2019-06-06 15:33:10 -0400349 public void setPulsing(boolean pulsing) {
350 if (mPulsing != pulsing) {
351 mPulsing = pulsing;
352 synchronized (mListeners) {
353 for (RankedListener rl : new ArrayList<>(mListeners)) {
354 rl.mListener.onPulsingChanged(pulsing);
355 }
356 }
357 }
358 }
359
Beverly8fdb5332019-02-04 14:29:49 -0500360 /**
361 * Returns String readable state of status bar from {@link StatusBarState}
362 */
Evan Laird91d0f102018-09-18 17:39:55 -0400363 public static String describe(int state) {
364 return StatusBarState.toShortString(state);
365 }
Evan Laird2ce144e2019-03-04 18:45:34 -0500366
367 @Override
368 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
369 pw.println("StatusBarStateController: ");
370 pw.println(" mState=" + mState + " (" + describe(mState) + ")");
371 pw.println(" mLastState=" + mLastState + " (" + describe(mLastState) + ")");
372 pw.println(" mLeaveOpenOnKeyguardHide=" + mLeaveOpenOnKeyguardHide);
373 pw.println(" mKeyguardRequested=" + mKeyguardRequested);
374 pw.println(" mIsDozing=" + mIsDozing);
375 pw.println(" Historical states:");
376 // Ignore records without a timestamp
377 int size = 0;
378 for (int i = 0; i < HISTORY_SIZE; i++) {
379 if (mHistoricalRecords[i].mTimestamp != 0) size++;
380 }
381 for (int i = mHistoryIndex + HISTORY_SIZE;
382 i >= mHistoryIndex + HISTORY_SIZE - size + 1; i--) {
383 pw.println(" (" + (mHistoryIndex + HISTORY_SIZE - i + 1) + ")"
384 + mHistoricalRecords[i & (HISTORY_SIZE - 1)]);
385 }
386 }
387
388 private void recordHistoricalState(int currentState, int lastState) {
389 mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
390 HistoricalState state = mHistoricalRecords[mHistoryIndex];
391 state.mState = currentState;
392 state.mLastState = lastState;
393 state.mTimestamp = System.currentTimeMillis();
394 }
395
396 /**
397 * For keeping track of our previous state to help with debugging
398 */
399 private static class HistoricalState {
400 int mState;
401 int mLastState;
402 long mTimestamp;
403
404 @Override
405 public String toString() {
406 if (mTimestamp != 0) {
407 StringBuilder sb = new StringBuilder();
408 sb.append("state=").append(mState)
409 .append(" (").append(describe(mState)).append(")");
410 sb.append("lastState=").append(mLastState).append(" (").append(describe(mLastState))
411 .append(")");
412 sb.append("timestamp=")
413 .append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp));
414
415 return sb.toString();
416 }
417 return "Empty " + getClass().getSimpleName();
418 }
419 }
Jason Monk1fd3fc32018-08-14 17:20:09 -0400420}