blob: eb386dcdbf461a6d58a18302a47bf9635f3e6a4d [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;
23import android.view.animation.Interpolator;
24
Evan Laird91d0f102018-09-18 17:39:55 -040025import com.android.internal.annotations.GuardedBy;
Evan Laird2ce144e2019-03-04 18:45:34 -050026import com.android.systemui.Dumpable;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080027import com.android.systemui.Interpolators;
Beverly8fdb5332019-02-04 14:29:49 -050028import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080029import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
Jason Monkaf08c152018-12-04 11:12:39 -050030import com.android.systemui.statusbar.policy.CallbackController;
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080031
Evan Laird2ce144e2019-03-04 18:45:34 -050032import java.io.FileDescriptor;
33import java.io.PrintWriter;
Evan Laird91d0f102018-09-18 17:39:55 -040034import java.util.ArrayList;
35import java.util.Comparator;
Jason Monk1fd3fc32018-08-14 17:20:09 -040036
Jason Monk27d01a622018-12-10 15:57:09 -050037import javax.inject.Inject;
38import javax.inject.Singleton;
39
Jason Monk1fd3fc32018-08-14 17:20:09 -040040/**
41 * Tracks and reports on {@link StatusBarState}.
42 */
Jason Monk27d01a622018-12-10 15:57:09 -050043@Singleton
Beverly8fdb5332019-02-04 14:29:49 -050044public class StatusBarStateControllerImpl implements SysuiStatusBarStateController,
Evan Laird2ce144e2019-03-04 18:45:34 -050045 CallbackController<StateListener>, Dumpable {
Evan Laird91d0f102018-09-18 17:39:55 -040046 private static final String TAG = "SbStateController";
Evan Laird2ce144e2019-03-04 18:45:34 -050047 // Must be a power of 2
48 private static final int HISTORY_SIZE = 32;
Jason Monk1fd3fc32018-08-14 17:20:09 -040049
50 private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
51 private static final int MIN_STATE = StatusBarState.SHADE;
52
Beverly8fdb5332019-02-04 14:29:49 -050053 private static final Comparator<RankedListener> sComparator =
54 Comparator.comparingInt(o -> o.mRank);
55 private static final FloatProperty<StatusBarStateControllerImpl> SET_DARK_AMOUNT_PROPERTY =
56 new FloatProperty<StatusBarStateControllerImpl>("mDozeAmount") {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080057
58 @Override
Beverly8fdb5332019-02-04 14:29:49 -050059 public void setValue(StatusBarStateControllerImpl object, float value) {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080060 object.setDozeAmountInternal(value);
61 }
62
63 @Override
Beverly8fdb5332019-02-04 14:29:49 -050064 public Float get(StatusBarStateControllerImpl object) {
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080065 return object.mDozeAmount;
66 }
67 };
Evan Laird91d0f102018-09-18 17:39:55 -040068
69 private final ArrayList<RankedListener> mListeners = new ArrayList<>();
Jason Monk1fd3fc32018-08-14 17:20:09 -040070 private int mState;
71 private int mLastState;
72 private boolean mLeaveOpenOnKeyguardHide;
Jason Monk297c04e2018-08-23 17:16:59 -040073 private boolean mKeyguardRequested;
Jason Monk1fd3fc32018-08-14 17:20:09 -040074
Evan Laird2ce144e2019-03-04 18:45:34 -050075 // Record the HISTORY_SIZE most recent states
76 private int mHistoryIndex = 0;
77 private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
78
Lucas Dupin1a8b33d2018-11-12 18:18:15 -080079 /**
80 * If the device is currently dozing or not.
81 */
82 private boolean mIsDozing;
83
84 /**
85 * Current {@link #mDozeAmount} animator.
86 */
87 private ValueAnimator mDarkAnimator;
88
89 /**
90 * Current doze amount in this frame.
91 */
92 private float mDozeAmount;
93
94 /**
95 * Where the animator will stop.
96 */
97 private float mDozeAmountTarget;
98
99 /**
100 * The type of interpolator that should be used to the doze animation.
101 */
102 private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
103
Jason Monk27d01a622018-12-10 15:57:09 -0500104 @Inject
Beverly8fdb5332019-02-04 14:29:49 -0500105 public StatusBarStateControllerImpl() {
Evan Laird2ce144e2019-03-04 18:45:34 -0500106 for (int i = 0; i < HISTORY_SIZE; i++) {
107 mHistoricalRecords[i] = new HistoricalState();
108 }
Jason Monk27d01a622018-12-10 15:57:09 -0500109 }
110
Beverly8fdb5332019-02-04 14:29:49 -0500111 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400112 public int getState() {
113 return mState;
114 }
115
Beverly8fdb5332019-02-04 14:29:49 -0500116 @Override
Evan Laird91d0f102018-09-18 17:39:55 -0400117 public boolean setState(int state) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400118 if (state > MAX_STATE || state < MIN_STATE) {
119 throw new IllegalArgumentException("Invalid state " + state);
120 }
121 if (state == mState) {
Evan Laird91d0f102018-09-18 17:39:55 -0400122 return false;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400123 }
Evan Laird2ce144e2019-03-04 18:45:34 -0500124
125 // Record the to-be mState and mLastState
126 recordHistoricalState(state, mState);
127
Jason Monk1fd3fc32018-08-14 17:20:09 -0400128 synchronized (mListeners) {
Evan Laird91d0f102018-09-18 17:39:55 -0400129 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500130 rl.mListener.onStatePreChange(mState, state);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400131 }
132 mLastState = mState;
133 mState = state;
Evan Laird91d0f102018-09-18 17:39:55 -0400134 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500135 rl.mListener.onStateChanged(mState);
Evan Laird91d0f102018-09-18 17:39:55 -0400136 }
137
138 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500139 rl.mListener.onStatePostChange();
Jason Monk1fd3fc32018-08-14 17:20:09 -0400140 }
141 }
Evan Laird91d0f102018-09-18 17:39:55 -0400142
143 return true;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400144 }
145
Beverly8fdb5332019-02-04 14:29:49 -0500146 @Override
Evan Lairde84d8552018-10-15 15:08:45 -0400147 public boolean isDozing() {
148 return mIsDozing;
149 }
150
Beverly8fdb5332019-02-04 14:29:49 -0500151 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800152 public float getDozeAmount() {
153 return mDozeAmount;
154 }
155
Beverly8fdb5332019-02-04 14:29:49 -0500156 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800157 public float getInterpolatedDozeAmount() {
158 return mDozeInterpolator.getInterpolation(mDozeAmount);
159 }
160
Beverly8fdb5332019-02-04 14:29:49 -0500161 @Override
Evan Lairde84d8552018-10-15 15:08:45 -0400162 public boolean setIsDozing(boolean isDozing) {
163 if (mIsDozing == isDozing) {
164 return false;
165 }
166
167 mIsDozing = isDozing;
168
169 synchronized (mListeners) {
170 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500171 rl.mListener.onDozingChanged(isDozing);
Evan Lairde84d8552018-10-15 15:08:45 -0400172 }
173 }
174
175 return true;
176 }
177
Beverly8fdb5332019-02-04 14:29:49 -0500178 @Override
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800179 public void setDozeAmount(float dozeAmount, boolean animated) {
180 if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
181 if (animated && mDozeAmountTarget == dozeAmount) {
182 return;
183 } else {
184 mDarkAnimator.cancel();
185 }
186 }
187
188 mDozeAmountTarget = dozeAmount;
189 if (animated) {
190 startDozeAnimation();
191 } else {
192 setDozeAmountInternal(dozeAmount);
193 }
194 }
195
196 private void startDozeAnimation() {
197 if (mDozeAmount == 0f || mDozeAmount == 1f) {
198 mDozeInterpolator = mIsDozing
199 ? Interpolators.FAST_OUT_SLOW_IN
200 : Interpolators.TOUCH_RESPONSE_REVERSE;
201 }
202 mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget);
203 mDarkAnimator.setInterpolator(Interpolators.LINEAR);
204 mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
205 mDarkAnimator.start();
206 }
207
208 private void setDozeAmountInternal(float dozeAmount) {
209 mDozeAmount = dozeAmount;
210 float interpolatedAmount = mDozeInterpolator.getInterpolation(dozeAmount);
211 synchronized (mListeners) {
212 for (RankedListener rl : new ArrayList<>(mListeners)) {
Beverly8fdb5332019-02-04 14:29:49 -0500213 rl.mListener.onDozeAmountChanged(mDozeAmount, interpolatedAmount);
Lucas Dupin1a8b33d2018-11-12 18:18:15 -0800214 }
215 }
216 }
217
Beverly8fdb5332019-02-04 14:29:49 -0500218 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400219 public boolean goingToFullShade() {
220 return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
221 }
222
Beverly8fdb5332019-02-04 14:29:49 -0500223 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400224 public void setLeaveOpenOnKeyguardHide(boolean leaveOpen) {
225 mLeaveOpenOnKeyguardHide = leaveOpen;
226 }
227
Beverly8fdb5332019-02-04 14:29:49 -0500228 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400229 public boolean leaveOpenOnKeyguardHide() {
230 return mLeaveOpenOnKeyguardHide;
231 }
232
Beverly8fdb5332019-02-04 14:29:49 -0500233 @Override
Jason Monk1fd3fc32018-08-14 17:20:09 -0400234 public boolean fromShadeLocked() {
235 return mLastState == StatusBarState.SHADE_LOCKED;
236 }
237
Beverly8fdb5332019-02-04 14:29:49 -0500238 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500239 public void addCallback(StateListener listener) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400240 synchronized (mListeners) {
Evan Laird91d0f102018-09-18 17:39:55 -0400241 addListenerInternalLocked(listener, Integer.MAX_VALUE);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400242 }
243 }
244
Evan Laird91d0f102018-09-18 17:39:55 -0400245 /**
246 * Add a listener and a rank based on the priority of this message
247 * @param listener the listener
248 * @param rank the order in which you'd like to be called. Ranked listeners will be
249 * notified before unranked, and we will sort ranked listeners from low to high
250 *
251 * @deprecated This method exists only to solve latent inter-dependencies from refactoring
252 * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
253 * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
254 */
Beverly8fdb5332019-02-04 14:29:49 -0500255 @Deprecated
256 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500257 public void addCallback(StateListener listener, @SbStateListenerRank int rank) {
Evan Laird91d0f102018-09-18 17:39:55 -0400258 synchronized (mListeners) {
259 addListenerInternalLocked(listener, rank);
260 }
261 }
262
263 @GuardedBy("mListeners")
264 private void addListenerInternalLocked(StateListener listener, int rank) {
265 // Protect against double-subscribe
266 for (RankedListener rl : mListeners) {
Beverly8fdb5332019-02-04 14:29:49 -0500267 if (rl.mListener.equals(listener)) {
Evan Laird91d0f102018-09-18 17:39:55 -0400268 return;
269 }
270 }
271
Beverly8fdb5332019-02-04 14:29:49 -0500272 RankedListener rl = new SysuiStatusBarStateController.RankedListener(listener, rank);
Evan Laird91d0f102018-09-18 17:39:55 -0400273 mListeners.add(rl);
Beverly8fdb5332019-02-04 14:29:49 -0500274 mListeners.sort(sComparator);
Evan Laird91d0f102018-09-18 17:39:55 -0400275 }
276
Beverly8fdb5332019-02-04 14:29:49 -0500277
278 @Override
Jason Monkaf08c152018-12-04 11:12:39 -0500279 public void removeCallback(StateListener listener) {
Jason Monk1fd3fc32018-08-14 17:20:09 -0400280 synchronized (mListeners) {
Beverly8fdb5332019-02-04 14:29:49 -0500281 mListeners.removeIf((it) -> it.mListener.equals(listener));
Jason Monk1fd3fc32018-08-14 17:20:09 -0400282 }
283 }
284
Beverly8fdb5332019-02-04 14:29:49 -0500285 @Override
Jason Monk297c04e2018-08-23 17:16:59 -0400286 public void setKeyguardRequested(boolean keyguardRequested) {
287 mKeyguardRequested = keyguardRequested;
288 }
289
Beverly8fdb5332019-02-04 14:29:49 -0500290 @Override
Jason Monk297c04e2018-08-23 17:16:59 -0400291 public boolean isKeyguardRequested() {
292 return mKeyguardRequested;
293 }
294
Beverly8fdb5332019-02-04 14:29:49 -0500295 /**
296 * Returns String readable state of status bar from {@link StatusBarState}
297 */
Evan Laird91d0f102018-09-18 17:39:55 -0400298 public static String describe(int state) {
299 return StatusBarState.toShortString(state);
300 }
Evan Laird2ce144e2019-03-04 18:45:34 -0500301
302 @Override
303 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
304 pw.println("StatusBarStateController: ");
305 pw.println(" mState=" + mState + " (" + describe(mState) + ")");
306 pw.println(" mLastState=" + mLastState + " (" + describe(mLastState) + ")");
307 pw.println(" mLeaveOpenOnKeyguardHide=" + mLeaveOpenOnKeyguardHide);
308 pw.println(" mKeyguardRequested=" + mKeyguardRequested);
309 pw.println(" mIsDozing=" + mIsDozing);
310 pw.println(" Historical states:");
311 // Ignore records without a timestamp
312 int size = 0;
313 for (int i = 0; i < HISTORY_SIZE; i++) {
314 if (mHistoricalRecords[i].mTimestamp != 0) size++;
315 }
316 for (int i = mHistoryIndex + HISTORY_SIZE;
317 i >= mHistoryIndex + HISTORY_SIZE - size + 1; i--) {
318 pw.println(" (" + (mHistoryIndex + HISTORY_SIZE - i + 1) + ")"
319 + mHistoricalRecords[i & (HISTORY_SIZE - 1)]);
320 }
321 }
322
323 private void recordHistoricalState(int currentState, int lastState) {
324 mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
325 HistoricalState state = mHistoricalRecords[mHistoryIndex];
326 state.mState = currentState;
327 state.mLastState = lastState;
328 state.mTimestamp = System.currentTimeMillis();
329 }
330
331 /**
332 * For keeping track of our previous state to help with debugging
333 */
334 private static class HistoricalState {
335 int mState;
336 int mLastState;
337 long mTimestamp;
338
339 @Override
340 public String toString() {
341 if (mTimestamp != 0) {
342 StringBuilder sb = new StringBuilder();
343 sb.append("state=").append(mState)
344 .append(" (").append(describe(mState)).append(")");
345 sb.append("lastState=").append(mLastState).append(" (").append(describe(mLastState))
346 .append(")");
347 sb.append("timestamp=")
348 .append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp));
349
350 return sb.toString();
351 }
352 return "Empty " + getClass().getSimpleName();
353 }
354 }
Jason Monk1fd3fc32018-08-14 17:20:09 -0400355}