blob: e81e5cae5bfcd339badc1082db1ace372aec84a4 [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;
Lucas Dupin8968f6a2019-08-09 17:41:15 -070027import com.android.systemui.DejankUtils;
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 /**
Jorim Jaggi956ca412019-01-07 14:49:14 +010082 * If any of the system bars is hidden.
Beverlye14f08e2019-06-06 15:33:10 -040083 */
Jorim Jaggi956ca412019-01-07 14:49:14 +010084 private boolean mIsFullscreen = false;
85
86 /**
87 * If the navigation bar can stay hidden when the display gets tapped.
88 */
89 private boolean mIsImmersive = false;
Beverlye14f08e2019-06-06 15:33:10 -040090
91 /**
92 * If the device is currently pulsing (AOD2).
93 */
94 private boolean mPulsing;
95
96 /**
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080097 * If the device is currently dozing or not.
98 */
99 private boolean mIsDozing;
100
101 /**
102 * Current {@link #mDozeAmount} animator.
103 */
104 private ValueAnimator mDarkAnimator;
105
106 /**
107 * Current doze amount in this frame.
108 */
109 private float mDozeAmount;
110
111 /**
112 * Where the animator will stop.
113 */
114 private float mDozeAmountTarget;
115
116 /**
117 * The type of interpolator that should be used to the doze animation.
118 */
119 private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
120
Jason Monk27d01a622018-12-10 15:57:09 -0500121 @Inject
Beverly8fdb5332019-02-04 14:29:49 -0500122 public StatusBarStateControllerImpl() {
Evan Laird2ce144e2019-03-04 18:45:34 -0500123 for (int i = 0; i < HISTORY_SIZE; i++) {
124 mHistoricalRecords[i] = new HistoricalState();
125 }
Jason Monk27d01a622018-12-10 15:57:09 -0500126 }
127
Beverly8fdb5332019-02-04 14:29:49 -0500128 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400129 public int getState() {
130 return mState;
131 }
132
Beverly8fdb5332019-02-04 14:29:49 -0500133 @Override
Evan Laird91d0f102018-09-18 17:39:55 -0400134 public boolean setState(int state) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400135 if (state > MAX_STATE || state < MIN_STATE) {
136 throw new IllegalArgumentException("Invalid state " + state);
137 }
138 if (state == mState) {
Evan Laird91d0f102018-09-18 17:39:55 -0400139 return false;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400140 }
Evan Laird2ce144e2019-03-04 18:45:34 -0500141
142 // Record the to-be mState and mLastState
143 recordHistoricalState(state, mState);
144
Lucas Dupine7ff7be2019-09-17 11:19:53 -0400145 // b/139259891
146 if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
147 Log.e(TAG, "Invalid state transition: SHADE -> SHADE_LOCKED", new Throwable());
148 }
149
Jason Monk1fd3fc32018-08-14 17:20:09 -0400150 synchronized (mListeners) {
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700151 String tag = getClass().getSimpleName() + "#setState(" + state + ")";
152 DejankUtils.startDetectingBlockingIpcs(tag);
Evan Laird91d0f102018-09-18 17:39:55 -0400153 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500154 rl.mListener.onStatePreChange(mState, state);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400155 }
156 mLastState = mState;
157 mState = state;
Evan Laird91d0f102018-09-18 17:39:55 -0400158 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500159 rl.mListener.onStateChanged(mState);
Evan Laird91d0f102018-09-18 17:39:55 -0400160 }
161
162 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500163 rl.mListener.onStatePostChange();
Jason Monk1fd3fc32018-08-14 17:20:09 -0400164 }
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700165 DejankUtils.stopDetectingBlockingIpcs(tag);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400166 }
Evan Laird91d0f102018-09-18 17:39:55 -0400167
168 return true;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400169 }
170
Beverly8fdb5332019-02-04 14:29:49 -0500171 @Override
Evan Lairde84d8552018-10-15 15:08:45 -0400172 public boolean isDozing() {
173 return mIsDozing;
174 }
175
Beverly8fdb5332019-02-04 14:29:49 -0500176 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800177 public float getDozeAmount() {
178 return mDozeAmount;
179 }
180
Beverly8fdb5332019-02-04 14:29:49 -0500181 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800182 public float getInterpolatedDozeAmount() {
183 return mDozeInterpolator.getInterpolation(mDozeAmount);
184 }
185
Beverly8fdb5332019-02-04 14:29:49 -0500186 @Override
Evan Lairde84d8552018-10-15 15:08:45 -0400187 public boolean setIsDozing(boolean isDozing) {
188 if (mIsDozing == isDozing) {
189 return false;
190 }
191
192 mIsDozing = isDozing;
193
194 synchronized (mListeners) {
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700195 String tag = getClass().getSimpleName() + "#setIsDozing";
196 DejankUtils.startDetectingBlockingIpcs(tag);
Evan Lairde84d8552018-10-15 15:08:45 -0400197 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500198 rl.mListener.onDozingChanged(isDozing);
Evan Lairde84d8552018-10-15 15:08:45 -0400199 }
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700200 DejankUtils.stopDetectingBlockingIpcs(tag);
Evan Lairde84d8552018-10-15 15:08:45 -0400201 }
202
203 return true;
204 }
205
Beverly8fdb5332019-02-04 14:29:49 -0500206 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800207 public void setDozeAmount(float dozeAmount, boolean animated) {
208 if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
209 if (animated && mDozeAmountTarget == dozeAmount) {
210 return;
211 } else {
212 mDarkAnimator.cancel();
213 }
214 }
215
216 mDozeAmountTarget = dozeAmount;
217 if (animated) {
218 startDozeAnimation();
219 } else {
220 setDozeAmountInternal(dozeAmount);
221 }
222 }
223
224 private void startDozeAnimation() {
225 if (mDozeAmount == 0f || mDozeAmount == 1f) {
226 mDozeInterpolator = mIsDozing
227 ? Interpolators.FAST_OUT_SLOW_IN
228 : Interpolators.TOUCH_RESPONSE_REVERSE;
229 }
230 mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget);
231 mDarkAnimator.setInterpolator(Interpolators.LINEAR);
232 mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
233 mDarkAnimator.start();
234 }
235
236 private void setDozeAmountInternal(float dozeAmount) {
237 mDozeAmount = dozeAmount;
238 float interpolatedAmount = mDozeInterpolator.getInterpolation(dozeAmount);
239 synchronized (mListeners) {
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700240 String tag = getClass().getSimpleName() + "#setDozeAmount";
241 DejankUtils.startDetectingBlockingIpcs(tag);
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800242 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500243 rl.mListener.onDozeAmountChanged(mDozeAmount, interpolatedAmount);
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800244 }
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700245 DejankUtils.stopDetectingBlockingIpcs(tag);
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800246 }
247 }
248
Beverly8fdb5332019-02-04 14:29:49 -0500249 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400250 public boolean goingToFullShade() {
251 return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
252 }
253
Beverly8fdb5332019-02-04 14:29:49 -0500254 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400255 public void setLeaveOpenOnKeyguardHide(boolean leaveOpen) {
256 mLeaveOpenOnKeyguardHide = leaveOpen;
257 }
258
Beverly8fdb5332019-02-04 14:29:49 -0500259 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400260 public boolean leaveOpenOnKeyguardHide() {
261 return mLeaveOpenOnKeyguardHide;
262 }
263
Beverly8fdb5332019-02-04 14:29:49 -0500264 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400265 public boolean fromShadeLocked() {
266 return mLastState == StatusBarState.SHADE_LOCKED;
267 }
268
Beverly8fdb5332019-02-04 14:29:49 -0500269 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500270 public void addCallback(StateListener listener) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400271 synchronized (mListeners) {
Evan Laird91d0f102018-09-18 17:39:55 -0400272 addListenerInternalLocked(listener, Integer.MAX_VALUE);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400273 }
274 }
275
Evan Laird91d0f102018-09-18 17:39:55 -0400276 /**
277 * Add a listener and a rank based on the priority of this message
278 * @param listener the listener
279 * @param rank the order in which you'd like to be called. Ranked listeners will be
280 * notified before unranked, and we will sort ranked listeners from low to high
281 *
282 * @deprecated This method exists only to solve latent inter-dependencies from refactoring
283 * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
284 * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
285 */
Beverly8fdb5332019-02-04 14:29:49 -0500286 @Deprecated
287 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500288 public void addCallback(StateListener listener, @SbStateListenerRank int rank) {
Evan Laird91d0f102018-09-18 17:39:55 -0400289 synchronized (mListeners) {
290 addListenerInternalLocked(listener, rank);
291 }
292 }
293
294 @GuardedBy("mListeners")
295 private void addListenerInternalLocked(StateListener listener, int rank) {
296 // Protect against double-subscribe
297 for (RankedListener rl : mListeners) {
Beverly8fdb5332019-02-04 14:29:49 -0500298 if (rl.mListener.equals(listener)) {
Evan Laird91d0f102018-09-18 17:39:55 -0400299 return;
300 }
301 }
302
Beverly8fdb5332019-02-04 14:29:49 -0500303 RankedListener rl = new SysuiStatusBarStateController.RankedListener(listener, rank);
Evan Laird91d0f102018-09-18 17:39:55 -0400304 mListeners.add(rl);
Beverly8fdb5332019-02-04 14:29:49 -0500305 mListeners.sort(sComparator);
Evan Laird91d0f102018-09-18 17:39:55 -0400306 }
307
Beverly8fdb5332019-02-04 14:29:49 -0500308
309 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500310 public void removeCallback(StateListener listener) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400311 synchronized (mListeners) {
Beverly8fdb5332019-02-04 14:29:49 -0500312 mListeners.removeIf((it) -> it.mListener.equals(listener));
Jason Monk1fd3fc32018-08-14 17:20:09 -0400313 }
314 }
315
Beverly8fdb5332019-02-04 14:29:49 -0500316 @Override
Jason Monk297c04e2018-08-23 17:16:59 -0400317 public void setKeyguardRequested(boolean keyguardRequested) {
318 mKeyguardRequested = keyguardRequested;
319 }
320
Beverly8fdb5332019-02-04 14:29:49 -0500321 @Override
Jason Monk297c04e2018-08-23 17:16:59 -0400322 public boolean isKeyguardRequested() {
323 return mKeyguardRequested;
324 }
325
Beverlye14f08e2019-06-06 15:33:10 -0400326 @Override
Jorim Jaggi956ca412019-01-07 14:49:14 +0100327 public void setFullscreenState(boolean isFullscreen, boolean isImmersive) {
328 if (mIsFullscreen != isFullscreen || mIsImmersive != isImmersive) {
329 mIsFullscreen = isFullscreen;
330 mIsImmersive = isImmersive;
Ioannis Ilkos8cca1412019-10-17 09:38:00 +0000331 synchronized (mListeners) {
332 for (RankedListener rl : new ArrayList<>(mListeners)) {
Jorim Jaggi956ca412019-01-07 14:49:14 +0100333 rl.mListener.onFullscreenStateChanged(isFullscreen, isImmersive);
Ioannis Ilkos8cca1412019-10-17 09:38:00 +0000334 }
335 }
336 }
337 }
338
339 @Override
Beverlye14f08e2019-06-06 15:33:10 -0400340 public void setPulsing(boolean pulsing) {
341 if (mPulsing != pulsing) {
342 mPulsing = pulsing;
343 synchronized (mListeners) {
344 for (RankedListener rl : new ArrayList<>(mListeners)) {
345 rl.mListener.onPulsingChanged(pulsing);
346 }
347 }
348 }
349 }
350
Beverly8fdb5332019-02-04 14:29:49 -0500351 /**
352 * Returns String readable state of status bar from {@link StatusBarState}
353 */
Evan Laird91d0f102018-09-18 17:39:55 -0400354 public static String describe(int state) {
355 return StatusBarState.toShortString(state);
356 }
Evan Laird2ce144e2019-03-04 18:45:34 -0500357
358 @Override
359 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
360 pw.println("StatusBarStateController: ");
361 pw.println(" mState=" + mState + " (" + describe(mState) + ")");
362 pw.println(" mLastState=" + mLastState + " (" + describe(mLastState) + ")");
363 pw.println(" mLeaveOpenOnKeyguardHide=" + mLeaveOpenOnKeyguardHide);
364 pw.println(" mKeyguardRequested=" + mKeyguardRequested);
365 pw.println(" mIsDozing=" + mIsDozing);
366 pw.println(" Historical states:");
367 // Ignore records without a timestamp
368 int size = 0;
369 for (int i = 0; i < HISTORY_SIZE; i++) {
370 if (mHistoricalRecords[i].mTimestamp != 0) size++;
371 }
372 for (int i = mHistoryIndex + HISTORY_SIZE;
373 i >= mHistoryIndex + HISTORY_SIZE - size + 1; i--) {
374 pw.println(" (" + (mHistoryIndex + HISTORY_SIZE - i + 1) + ")"
375 + mHistoricalRecords[i & (HISTORY_SIZE - 1)]);
376 }
377 }
378
379 private void recordHistoricalState(int currentState, int lastState) {
380 mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
381 HistoricalState state = mHistoricalRecords[mHistoryIndex];
382 state.mState = currentState;
383 state.mLastState = lastState;
384 state.mTimestamp = System.currentTimeMillis();
385 }
386
387 /**
388 * For keeping track of our previous state to help with debugging
389 */
390 private static class HistoricalState {
391 int mState;
392 int mLastState;
393 long mTimestamp;
394
395 @Override
396 public String toString() {
397 if (mTimestamp != 0) {
398 StringBuilder sb = new StringBuilder();
399 sb.append("state=").append(mState)
400 .append(" (").append(describe(mState)).append(")");
401 sb.append("lastState=").append(mLastState).append(" (").append(describe(mLastState))
402 .append(")");
403 sb.append("timestamp=")
404 .append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp));
405
406 return sb.toString();
407 }
408 return "Empty " + getClass().getSimpleName();
409 }
410 }
Jason Monk1fd3fc32018-08-14 17:20:09 -0400411}