blob: f8e1093e210b12322be6679e29060ec79a9a8633 [file] [log] [blame]
Daniel Sandler50a53132012-10-24 15:02:27 -04001/*
2 * Copyright (C) 2012 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
Daniel Sandler08d05e32012-08-08 16:39:54 -040017package com.android.systemui.statusbar.phone;
18
Jorim Jaggi1d480692014-05-20 19:41:58 +020019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040021import android.animation.ObjectAnimator;
Jorim Jaggi1d480692014-05-20 19:41:58 +020022import android.animation.ValueAnimator;
Daniel Sandler08d05e32012-08-08 16:39:54 -040023import android.content.Context;
Selim Cinekb84a1072014-05-15 19:10:18 +020024import android.content.res.Configuration;
Daniel Sandler08d05e32012-08-08 16:39:54 -040025import android.content.res.Resources;
Selim Cinek48ff9b42016-11-09 19:31:51 -080026import android.os.SystemClock;
Selim Cinekd95ca7c2017-07-26 12:20:38 -070027import android.os.VibrationEffect;
Daniel Sandler08d05e32012-08-08 16:39:54 -040028import android.util.AttributeSet;
John Spurlockcd686b52013-06-05 10:13:46 -040029import android.util.Log;
Jun Mukaide750b42015-10-01 18:35:33 +090030import android.view.InputDevice;
Daniel Sandler08d05e32012-08-08 16:39:54 -040031import android.view.MotionEvent;
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -070032import android.view.VelocityTracker;
Adrian Roos8d4e99f2017-05-25 18:03:58 -070033import android.view.View;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010034import android.view.ViewConfiguration;
Jorim Jaggi0a27be82014-06-11 03:22:39 +020035import android.view.ViewTreeObserver;
Jorim Jaggi90129582014-06-02 14:44:49 +020036import android.view.animation.Interpolator;
Daniel Sandler08d05e32012-08-08 16:39:54 -040037import android.widget.FrameLayout;
38
Chris Wren27a52fa2017-02-01 14:21:43 -050039import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monkea03be12017-12-04 11:08:41 -050040import com.android.internal.util.LatencyTracker;
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +020041import com.android.systemui.DejankUtils;
Matthew Nge2b4d792018-03-09 13:42:18 -080042import com.android.systemui.Dependency;
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +020043import com.android.systemui.Interpolators;
Daniel Sandler08d05e32012-08-08 16:39:54 -040044import com.android.systemui.R;
Dave Mankoffdde5ee62019-05-02 17:36:11 -040045import com.android.systemui.classifier.FalsingManagerFactory;
John Spurlock813552c2014-09-19 08:30:21 -040046import com.android.systemui.doze.DozeLog;
Dave Mankoff468d4f62019-05-08 14:56:29 -040047import com.android.systemui.plugins.FalsingManager;
Beverly8fdb5332019-02-04 14:29:49 -050048import com.android.systemui.plugins.statusbar.StatusBarStateController;
Jorim Jaggi1d480692014-05-20 19:41:58 +020049import com.android.systemui.statusbar.FlingAnimationUtils;
Jorim Jaggi90129582014-06-02 14:44:49 +020050import com.android.systemui.statusbar.StatusBarState;
Beverly8fdb5332019-02-04 14:29:49 -050051import com.android.systemui.statusbar.SysuiStatusBarStateController;
Matthew Ng5c0592e2018-03-08 14:51:36 -080052import com.android.systemui.statusbar.VibratorHelper;
Jason Monk1fd3fc32018-08-14 17:20:09 -040053import com.android.systemui.statusbar.policy.KeyguardMonitor;
Daniel Sandler08d05e32012-08-08 16:39:54 -040054
John Spurlockde84f0e2013-06-12 12:41:00 -040055import java.io.FileDescriptor;
56import java.io.PrintWriter;
Selim Cineka678b0e2019-07-23 13:12:55 -070057import java.util.ArrayList;
John Spurlockde84f0e2013-06-12 12:41:00 -040058
Selim Cinek4c6969a2014-05-26 19:22:17 +020059public abstract class PanelView extends FrameLayout {
Daniel Sandler198a0302012-08-17 16:04:31 -040060 public static final boolean DEBUG = PanelBar.DEBUG;
Daniel Sandler08d05e32012-08-08 16:39:54 -040061 public static final String TAG = PanelView.class.getSimpleName();
Selim Cinek48ff9b42016-11-09 19:31:51 -080062 private static final int INITIAL_OPENING_PEEK_DURATION = 200;
63 private static final int PEEK_ANIMATION_DURATION = 360;
Selim Cinek2627d722018-01-19 12:16:49 -080064 private static final int NO_FIXED_DURATION = -1;
Selim Cinek08bd435a2019-07-26 11:37:14 -070065 protected long mDownTime;
66 protected boolean mTouchSlopExceededBeforeDown;
Selim Cinek48ff9b42016-11-09 19:31:51 -080067 private float mMinExpandHeight;
Chris Wren27a52fa2017-02-01 14:21:43 -050068 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
Selim Cinekbe2c4432017-05-30 12:11:09 -070069 private boolean mPanelUpdateWhenAnimatorEnds;
Selim Cinekf6559f82017-08-16 14:49:10 -070070 private boolean mVibrateOnOpening;
Selim Cinek2627d722018-01-19 12:16:49 -080071 protected boolean mLaunchingNotification;
72 private int mFixedDuration = NO_FIXED_DURATION;
Selim Cineka678b0e2019-07-23 13:12:55 -070073 protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>();
Selim Cinek173f2d02015-04-29 15:36:56 -070074
John Spurlock97642182013-07-29 17:58:39 -040075 private final void logf(String fmt, Object... args) {
John Spurlockcd686b52013-06-05 10:13:46 -040076 Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
Daniel Sandler08d05e32012-08-08 16:39:54 -040077 }
78
Jason Monk2a6ea9c2017-01-26 11:14:51 -050079 protected StatusBar mStatusBar;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090080 protected HeadsUpManagerPhone mHeadsUpManager;
Selim Cinek684a4422015-04-15 16:18:39 -070081
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040082 private float mPeekHeight;
Jorim Jaggi90129582014-06-02 14:44:49 +020083 private float mHintDistance;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010084 private float mInitialOffsetOnTouch;
Selim Cinek79d79c42015-05-21 16:14:45 -070085 private boolean mCollapsedAndHeadsUpOnDown;
Daniel Sandler08d05e32012-08-08 16:39:54 -040086 private float mExpandedFraction = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +020087 protected float mExpandedHeight = 0;
Selim Cinek31094df2014-08-14 19:28:15 +020088 private boolean mPanelClosedOnDown;
89 private boolean mHasLayoutedSinceDown;
90 private float mUpdateFlingVelocity;
91 private boolean mUpdateFlingOnLayout;
Jorim Jaggib7a33032014-08-20 16:21:36 +020092 private boolean mPeekTouching;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040093 private boolean mJustPeeked;
Daniel Sandler50508132012-08-16 14:10:53 -040094 private boolean mClosing;
Jorim Jaggi8dd95e02014-06-03 16:19:33 +020095 protected boolean mTracking;
Jorim Jaggi90129582014-06-02 14:44:49 +020096 private boolean mTouchSlopExceeded;
John Spurlock48fa91a2013-08-15 09:29:31 -040097 private int mTrackingPointer;
Jorim Jaggid7daab72014-05-06 22:22:20 +020098 protected int mTouchSlop;
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +020099 protected boolean mHintAnimationRunning;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200100 private boolean mOverExpandedBeforeFling;
Selim Cinek19c8c702014-08-25 22:09:19 +0200101 private boolean mTouchAboveFalsingThreshold;
102 private int mUnlockFalsingThreshold;
Selim Cinekab1dc952014-10-30 20:20:29 +0100103 private boolean mTouchStartedInEmptyArea;
Selim Cinek547a06b2014-11-27 14:06:04 +0100104 private boolean mMotionAborted;
Selim Cinek9db71052015-04-24 18:54:30 -0700105 private boolean mUpwardsWhenTresholdReached;
Selim Cinek173f2d02015-04-29 15:36:56 -0700106 private boolean mAnimatingOnDown;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400107
Jorim Jaggi1d480692014-05-20 19:41:58 +0200108 private ValueAnimator mHeightAnimator;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400109 private ObjectAnimator mPeekAnimator;
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700110 private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
Jorim Jaggi1d480692014-05-20 19:41:58 +0200111 private FlingAnimationUtils mFlingAnimationUtils;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800112 private FlingAnimationUtils mFlingAnimationUtilsClosing;
Selim Cinek2411f762016-12-28 17:05:08 +0100113 private FlingAnimationUtils mFlingAnimationUtilsDismissing;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700114 private FalsingManager mFalsingManager;
Matthew Ng5c0592e2018-03-08 14:51:36 -0800115 private final VibratorHelper mVibratorHelper;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400116
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200117 /**
118 * Whether an instant expand request is currently pending and we are just waiting for layout.
119 */
120 private boolean mInstantExpanding;
Jorim Jaggi6626f542016-08-22 13:08:44 -0700121 private boolean mAnimateAfterExpanding;
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200122
Daniel Sandler08d05e32012-08-08 16:39:54 -0400123 PanelBar mBar;
124
Daniel Sandler50508132012-08-16 14:10:53 -0400125 private String mViewName;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200126 private float mInitialTouchY;
127 private float mInitialTouchX;
Jorim Jaggid41083a2014-09-12 02:54:40 +0200128 private boolean mTouchDisabled;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400129
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800130 /**
131 * Whether or not the PanelView can be expanded or collapsed with a drag.
132 */
133 private boolean mNotificationsDragEnabled;
134
Jorim Jaggi90129582014-06-02 14:44:49 +0200135 private Interpolator mBounceInterpolator;
Selim Cinekf99d0002014-06-13 07:36:01 +0200136 protected KeyguardBottomAreaView mKeyguardBottomArea;
Jorim Jaggi90129582014-06-02 14:44:49 +0200137
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700138 /**
139 * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time.
140 */
141 private float mNextCollapseSpeedUpFactor = 1.0f;
142
Jorim Jaggi1d071ce2015-07-22 14:05:06 -0700143 protected boolean mExpanding;
Jorim Jaggib472b3472014-06-30 19:56:24 +0200144 private boolean mGestureWaitForTouchSlop;
Selim Cinek9db71052015-04-24 18:54:30 -0700145 private boolean mIgnoreXTouchSlop;
Jorim Jaggie05256e2016-09-08 14:58:20 -0700146 private boolean mExpandLatencyTracking;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400147 protected final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
Beverly8fdb5332019-02-04 14:29:49 -0500148 protected final SysuiStatusBarStateController mStatusBarStateController =
149 (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200150
Selim Cinek1685e632014-04-08 02:27:49 +0200151 protected void onExpandingFinished() {
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200152 mBar.onExpandingFinished();
Selim Cinek1685e632014-04-08 02:27:49 +0200153 }
154
155 protected void onExpandingStarted() {
156 }
157
Jorim Jaggib472b3472014-06-30 19:56:24 +0200158 private void notifyExpandingStarted() {
159 if (!mExpanding) {
160 mExpanding = true;
161 onExpandingStarted();
162 }
163 }
164
Jorim Jaggi1d071ce2015-07-22 14:05:06 -0700165 protected final void notifyExpandingFinished() {
Selim Cinekb0e4f9e2015-11-02 13:42:58 -0800166 endClosing();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200167 if (mExpanding) {
168 mExpanding = false;
169 onExpandingFinished();
170 }
171 }
172
Selim Cinek48ff9b42016-11-09 19:31:51 -0800173 private void runPeekAnimation(long duration, float peekHeight, boolean collapseWhenFinished) {
174 mPeekHeight = peekHeight;
John Spurlock97642182013-07-29 17:58:39 -0400175 if (DEBUG) logf("peek to height=%.1f", mPeekHeight);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200176 if (mHeightAnimator != null) {
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400177 return;
178 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800179 if (mPeekAnimator != null) {
180 mPeekAnimator.cancel();
181 }
Jorim Jaggib472b3472014-06-30 19:56:24 +0200182 mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight)
Selim Cinek48ff9b42016-11-09 19:31:51 -0800183 .setDuration(duration);
Selim Cinekc18010f2016-01-20 13:41:30 -0800184 mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200185 mPeekAnimator.addListener(new AnimatorListenerAdapter() {
186 private boolean mCancelled;
187
188 @Override
189 public void onAnimationCancel(Animator animation) {
190 mCancelled = true;
191 }
192
193 @Override
194 public void onAnimationEnd(Animator animation) {
195 mPeekAnimator = null;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800196 if (!mCancelled && collapseWhenFinished) {
Selim Cineka0f5c762015-06-22 14:44:46 -0400197 postOnAnimation(mPostCollapseRunnable);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200198 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800199
Jorim Jaggib472b3472014-06-30 19:56:24 +0200200 }
201 });
202 notifyExpandingStarted();
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400203 mPeekAnimator.start();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200204 mJustPeeked = true;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400205 }
206
Daniel Sandler08d05e32012-08-08 16:39:54 -0400207 public PanelView(Context context, AttributeSet attrs) {
208 super(context, attrs);
Selim Cinek593ee202016-12-28 15:01:16 +0100209 mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f /* maxLengthSeconds */,
210 0.6f /* speedUpFactor */);
211 mFlingAnimationUtilsClosing = new FlingAnimationUtils(context, 0.5f /* maxLengthSeconds */,
212 0.6f /* speedUpFactor */);
Selim Cinek2411f762016-12-28 17:05:08 +0100213 mFlingAnimationUtilsDismissing = new FlingAnimationUtils(context,
214 0.5f /* maxLengthSeconds */, 0.2f /* speedUpFactor */, 0.6f /* x2 */,
215 0.84f /* y2 */);
Jorim Jaggi90129582014-06-02 14:44:49 +0200216 mBounceInterpolator = new BounceInterpolator();
Dave Mankoffdde5ee62019-05-02 17:36:11 -0400217 mFalsingManager = FalsingManagerFactory.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800218 mNotificationsDragEnabled =
219 getResources().getBoolean(R.bool.config_enableNotificationShadeDrag);
Matthew Nge2b4d792018-03-09 13:42:18 -0800220 mVibratorHelper = Dependency.get(VibratorHelper.class);
Selim Cinekf6559f82017-08-16 14:49:10 -0700221 mVibrateOnOpening = mContext.getResources().getBoolean(
222 R.bool.config_vibrateOnIconAnimation);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400223 }
224
Jorim Jaggi069cd032014-05-15 03:09:01 +0200225 protected void loadDimens() {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400226 final Resources res = getContext().getResources();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100227 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
228 mTouchSlop = configuration.getScaledTouchSlop();
Jorim Jaggi90129582014-06-02 14:44:49 +0200229 mHintDistance = res.getDimension(R.dimen.hint_move_distance);
Selim Cinek19c8c702014-08-25 22:09:19 +0200230 mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400231 }
232
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700233 private void addMovement(MotionEvent event) {
234 // Add movement to velocity tracker using raw screen X and Y coordinates instead
235 // of window coordinates because the window frame may be moving at the same time.
236 float deltaX = event.getRawX() - event.getX();
237 float deltaY = event.getRawY() - event.getY();
238 event.offsetLocation(deltaX, deltaY);
239 mVelocityTracker.addMovement(event);
240 event.offsetLocation(-deltaX, -deltaY);
241 }
242
Jason Monke59dc402018-08-16 12:05:01 -0400243 public void setTouchAndAnimationDisabled(boolean disabled) {
Jorim Jaggid41083a2014-09-12 02:54:40 +0200244 mTouchDisabled = disabled;
Selim Cinek48a92a52017-05-02 20:02:30 -0700245 if (mTouchDisabled) {
246 cancelHeightAnimator();
247 if (mTracking) {
248 onTrackingStopped(true /* expanded */);
249 }
Selim Cinekeede5d72017-05-22 17:09:55 -0700250 notifyExpandingFinished();
Selim Cinek3127daf02016-07-22 18:04:23 -0700251 }
Jorim Jaggid41083a2014-09-12 02:54:40 +0200252 }
253
Jorim Jaggie05256e2016-09-08 14:58:20 -0700254 public void startExpandLatencyTracking() {
255 if (LatencyTracker.isEnabled(mContext)) {
256 LatencyTracker.getInstance(mContext).onActionStart(
257 LatencyTracker.ACTION_EXPAND_PANEL);
258 mExpandLatencyTracking = true;
259 }
260 }
261
Daniel Sandlerbf526d12012-09-04 22:56:44 -0400262 @Override
263 public boolean onTouchEvent(MotionEvent event) {
lumark44f9e5a2018-05-07 21:49:35 +0800264 if (mInstantExpanding
265 || (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL)
Selim Cinek547a06b2014-11-27 14:06:04 +0100266 || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200267 return false;
268 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100269
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800270 // If dragging should not expand the notifications shade, then return false.
271 if (!mNotificationsDragEnabled) {
272 if (mTracking) {
273 // Turn off tracking if it's on or the shade can get stuck in the down position.
274 onTrackingStopped(true /* expand */);
275 }
276 return false;
277 }
278
Jun Mukaicf9933c2015-07-08 17:34:26 -0700279 // On expanding, single mouse click expands the panel instead of dragging.
Jun Mukaic99243a2015-10-15 02:59:57 -0700280 if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
Jun Mukaicf9933c2015-07-08 17:34:26 -0700281 if (event.getAction() == MotionEvent.ACTION_UP) {
282 expand(true);
283 }
284 return true;
285 }
286
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100287 /*
288 * We capture touch events here and update the expand height here in case according to
289 * the users fingers. This also handles multi-touch.
290 *
Chris Wren5d53df42015-06-26 11:26:03 -0400291 * If the user just clicks shortly, we show a quick peek of the shade.
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100292 *
293 * Flinging is also enabled in order to open or close the shade.
294 */
295
296 int pointerIndex = event.findPointerIndex(mTrackingPointer);
297 if (pointerIndex < 0) {
298 pointerIndex = 0;
299 mTrackingPointer = event.getPointerId(pointerIndex);
300 }
Jorim Jaggia6310292014-04-16 14:11:52 +0200301 final float x = event.getX(pointerIndex);
Selim Cinek547a06b2014-11-27 14:06:04 +0100302 final float y = event.getY(pointerIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100303
Jorim Jaggib472b3472014-06-30 19:56:24 +0200304 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
Sunny Goyalb59e15e2019-06-13 17:02:32 -0700305 mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
Selim Cinek684a4422015-04-15 16:18:39 -0700306 mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200307 }
Selim Cinek4c6969a2014-05-26 19:22:17 +0200308
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100309 switch (event.getActionMasked()) {
310 case MotionEvent.ACTION_DOWN:
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700311 startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
Jorim Jaggi3857ac42014-06-27 18:01:12 +0200312 mJustPeeked = false;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800313 mMinExpandHeight = 0.0f;
Selim Cinek684a4422015-04-15 16:18:39 -0700314 mPanelClosedOnDown = isFullyCollapsed();
Selim Cinek31094df2014-08-14 19:28:15 +0200315 mHasLayoutedSinceDown = false;
316 mUpdateFlingOnLayout = false;
Selim Cinek547a06b2014-11-27 14:06:04 +0100317 mMotionAborted = false;
Jorim Jaggib7a33032014-08-20 16:21:36 +0200318 mPeekTouching = mPanelClosedOnDown;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800319 mDownTime = SystemClock.uptimeMillis();
Selim Cinek19c8c702014-08-25 22:09:19 +0200320 mTouchAboveFalsingThreshold = false;
Selim Cinek79d79c42015-05-21 16:14:45 -0700321 mCollapsedAndHeadsUpOnDown = isFullyCollapsed()
322 && mHeadsUpManager.hasPinnedHeadsUp();
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700323 addMovement(event);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800324 if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)
325 || mPeekAnimator != null) {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200326 mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
Selim Cinek08bd435a2019-07-26 11:37:14 -0700327 || mPeekAnimator != null || mTouchSlopExceededBeforeDown;
Selim Cinekd18b86f2017-06-21 09:44:52 -0700328 cancelHeightAnimator();
329 cancelPeek();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200330 onTrackingStarted();
Jorim Jaggi1d480692014-05-20 19:41:58 +0200331 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -0800332 if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
333 && !mStatusBar.isBouncerShowing()) {
Evan Laird404a85c2018-02-28 15:31:29 -0500334 startOpening(event);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100335 }
336 break;
337
338 case MotionEvent.ACTION_POINTER_UP:
339 final int upPointer = event.getPointerId(event.getActionIndex());
340 if (mTrackingPointer == upPointer) {
341 // gesture is ongoing, find a new pointer to track
342 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
343 final float newY = event.getY(newIndex);
Jorim Jaggia6310292014-04-16 14:11:52 +0200344 final float newX = event.getX(newIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100345 mTrackingPointer = event.getPointerId(newIndex);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700346 startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100347 }
348 break;
Selim Cinek547a06b2014-11-27 14:06:04 +0100349 case MotionEvent.ACTION_POINTER_DOWN:
Jason Monk1fd3fc32018-08-14 17:20:09 -0400350 if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
Selim Cinek547a06b2014-11-27 14:06:04 +0100351 mMotionAborted = true;
352 endMotionEvent(event, x, y, true /* forceCancel */);
353 return false;
354 }
355 break;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100356 case MotionEvent.ACTION_MOVE:
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700357 addMovement(event);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200358 float h = y - mInitialTouchY;
Jorim Jaggib7240132014-06-30 01:39:07 +0200359
360 // If the panel was collapsed when touching, we only need to check for the
361 // y-component of the gesture, as we have no conflicting horizontal gesture.
362 if (Math.abs(h) > mTouchSlop
363 && (Math.abs(h) > Math.abs(x - mInitialTouchX)
Selim Cinek48ff9b42016-11-09 19:31:51 -0800364 || mIgnoreXTouchSlop)) {
Jorim Jaggi90129582014-06-02 14:44:49 +0200365 mTouchSlopExceeded = true;
Selim Cinek79d79c42015-05-21 16:14:45 -0700366 if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
Jorim Jaggi0b1528a2014-10-28 22:47:46 +0100367 if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700368 startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200369 h = 0;
370 }
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200371 cancelHeightAnimator();
Jorim Jaggi90129582014-06-02 14:44:49 +0200372 onTrackingStarted();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200373 }
Selim Cinek4c6969a2014-05-26 19:22:17 +0200374 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800375 float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200376 if (newHeight > mPeekHeight) {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200377 if (mPeekAnimator != null) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100378 mPeekAnimator.cancel();
379 }
380 mJustPeeked = false;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800381 } else if (mPeekAnimator == null && mJustPeeked) {
382 // The initial peek has finished, but we haven't dragged as far yet, lets
383 // speed it up by starting at the peek height.
384 mInitialOffsetOnTouch = mExpandedHeight;
385 mInitialTouchY = y;
386 mMinExpandHeight = mExpandedHeight;
387 mJustPeeked = false;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100388 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800389 newHeight = Math.max(newHeight, mMinExpandHeight);
Selim Cinek29ed3c92014-09-23 20:44:35 +0200390 if (-h >= getFalsingThreshold()) {
Selim Cinek19c8c702014-08-25 22:09:19 +0200391 mTouchAboveFalsingThreshold = true;
Selim Cinek9db71052015-04-24 18:54:30 -0700392 mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);
Selim Cinek19c8c702014-08-25 22:09:19 +0200393 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800394 if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) &&
395 !isTrackingBlocked()) {
Jorim Jaggicc693242014-06-14 03:04:35 +0000396 setExpandedHeightInternal(newHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100397 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100398 break;
399
400 case MotionEvent.ACTION_UP:
401 case MotionEvent.ACTION_CANCEL:
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700402 addMovement(event);
Selim Cinek547a06b2014-11-27 14:06:04 +0100403 endMotionEvent(event, x, y, false /* forceCancel */);
404 break;
405 }
Selim Cinek9db71052015-04-24 18:54:30 -0700406 return !mGestureWaitForTouchSlop || mTracking;
407 }
408
Evan Laird404a85c2018-02-28 15:31:29 -0500409 private void startOpening(MotionEvent event) {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800410 runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
411 false /* collapseWhenFinished */);
412 notifyBarPanelExpansionChanged();
Selim Cinek08bd435a2019-07-26 11:37:14 -0700413 maybeVibrateOnOpening();
Evan Laird404a85c2018-02-28 15:31:29 -0500414
415 //TODO: keyguard opens QS a different way; log that too?
416
417 // Log the position of the swipe that opened the panel
418 float width = mStatusBar.getDisplayWidth();
419 float height = mStatusBar.getDisplayHeight();
420 int rot = mStatusBar.getRotation();
421
422 mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
423 (int) (event.getX() / width * 100),
424 (int) (event.getY() / height * 100),
425 rot);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800426 }
427
Selim Cinek08bd435a2019-07-26 11:37:14 -0700428 protected void maybeVibrateOnOpening() {
429 if (mVibrateOnOpening) {
430 mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
431 }
432 }
433
Selim Cinek48ff9b42016-11-09 19:31:51 -0800434 protected abstract float getOpeningHeight();
435
Selim Cinek9db71052015-04-24 18:54:30 -0700436 /**
437 * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
438 * horizontal direction
439 */
440 private boolean isDirectionUpwards(float x, float y) {
441 float xDiff = x - mInitialTouchX;
442 float yDiff = y - mInitialTouchY;
443 if (yDiff >= 0) {
444 return false;
445 }
446 return Math.abs(yDiff) >= Math.abs(xDiff);
Selim Cinek547a06b2014-11-27 14:06:04 +0100447 }
448
Chris Wren621933f2017-06-14 15:59:03 -0400449 protected void startExpandingFromPeek() {
450 mStatusBar.handlePeekToExpandTransistion();
451 }
452
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700453 protected void startExpandMotion(float newX, float newY, boolean startTracking,
454 float expandedHeight) {
455 mInitialOffsetOnTouch = expandedHeight;
456 mInitialTouchY = newY;
457 mInitialTouchX = newX;
458 if (startTracking) {
459 mTouchSlopExceeded = true;
Selim Cinekdef35a82016-05-03 15:52:51 -0700460 setExpandedHeight(mInitialOffsetOnTouch);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700461 onTrackingStarted();
462 }
463 }
464
Selim Cinek547a06b2014-11-27 14:06:04 +0100465 private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
466 mTrackingPointer = -1;
467 if ((mTracking && mTouchSlopExceeded)
468 || Math.abs(x - mInitialTouchX) > mTouchSlop
469 || Math.abs(y - mInitialTouchY) > mTouchSlop
470 || event.getActionMasked() == MotionEvent.ACTION_CANCEL
471 || forceCancel) {
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700472 mVelocityTracker.computeCurrentVelocity(1000);
473 float vel = mVelocityTracker.getYVelocity();
474 float vectorVel = (float) Math.hypot(
475 mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
476
Selim Cinek9db71052015-04-24 18:54:30 -0700477 boolean expand = flingExpands(vel, vectorVel, x, y)
Selim Cinek547a06b2014-11-27 14:06:04 +0100478 || event.getActionMasked() == MotionEvent.ACTION_CANCEL
479 || forceCancel;
Selim Cinek547a06b2014-11-27 14:06:04 +0100480 DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
481 mStatusBar.isFalsingThresholdNeeded(),
Jorim Jaggi50ff3af2015-08-12 18:35:42 -0700482 mStatusBar.isWakeUpComingFromTouch());
Christoph Studerb0183992014-12-22 21:02:26 +0100483 // Log collapse gesture if on lock screen.
Jason Monk1fd3fc32018-08-14 17:20:09 -0400484 if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
Christoph Studerb0183992014-12-22 21:02:26 +0100485 float displayDensity = mStatusBar.getDisplayDensity();
486 int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
487 int velocityDp = (int) Math.abs(vel / displayDensity);
Chris Wren27a52fa2017-02-01 14:21:43 -0500488 mLockscreenGestureLogger.write(
489 MetricsEvent.ACTION_LS_UNLOCK,
Christoph Studerb0183992014-12-22 21:02:26 +0100490 heightDp, velocityDp);
491 }
Selim Cinek9db71052015-04-24 18:54:30 -0700492 fling(vel, expand, isFalseTouch(x, y));
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700493 onTrackingStopped(expand);
Selim Cinek547a06b2014-11-27 14:06:04 +0100494 mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
495 if (mUpdateFlingOnLayout) {
496 mUpdateFlingVelocity = vel;
497 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -0800498 } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
Jason Monk1fd3fc32018-08-14 17:20:09 -0400499 && !mStatusBar.isBouncerShowing() && !mKeyguardMonitor.isKeyguardFadingAway()) {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800500 long timePassed = SystemClock.uptimeMillis() - mDownTime;
501 if (timePassed < ViewConfiguration.getLongPressTimeout()) {
502 // Lets show the user that he can actually expand the panel
503 runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
504 } else {
505 // We need to collapse the panel since we peeked to the small height.
506 postOnAnimation(mPostCollapseRunnable);
507 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -0800508 } else if (!mStatusBar.isBouncerShowing()) {
Selim Cinek547a06b2014-11-27 14:06:04 +0100509 boolean expands = onEmptySpaceClick(mInitialTouchX);
510 onTrackingStopped(expands);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100511 }
Selim Cinek547a06b2014-11-27 14:06:04 +0100512
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700513 mVelocityTracker.clear();
Selim Cinek547a06b2014-11-27 14:06:04 +0100514 mPeekTouching = false;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100515 }
516
Selim Cinekd5ab6452016-12-08 16:34:00 -0800517 protected float getCurrentExpandVelocity() {
Selim Cinekd5ab6452016-12-08 16:34:00 -0800518 mVelocityTracker.computeCurrentVelocity(1000);
519 return mVelocityTracker.getYVelocity();
520 }
521
Selim Cinek29ed3c92014-09-23 20:44:35 +0200522 private int getFalsingThreshold() {
Jorim Jaggi50ff3af2015-08-12 18:35:42 -0700523 float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinek29ed3c92014-09-23 20:44:35 +0200524 return (int) (mUnlockFalsingThreshold * factor);
525 }
526
Sunny Goyalb59e15e2019-06-13 17:02:32 -0700527 protected abstract boolean shouldGestureWaitForTouchSlop();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200528
Selim Cinek9db71052015-04-24 18:54:30 -0700529 protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
530
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200531 protected void onTrackingStopped(boolean expand) {
Selim Cinek4c6969a2014-05-26 19:22:17 +0200532 mTracking = false;
Xiaohui Chen9f967112016-01-07 14:14:06 -0800533 mBar.onTrackingStopped(expand);
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700534 notifyBarPanelExpansionChanged();
Selim Cinek1685e632014-04-08 02:27:49 +0200535 }
536
537 protected void onTrackingStarted() {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200538 endClosing();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200539 mTracking = true;
Xiaohui Chen9f967112016-01-07 14:14:06 -0800540 mBar.onTrackingStarted();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200541 notifyExpandingStarted();
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700542 notifyBarPanelExpansionChanged();
Selim Cinek1685e632014-04-08 02:27:49 +0200543 }
544
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100545 @Override
546 public boolean onInterceptTouchEvent(MotionEvent event) {
Selim Cinekbe2c4432017-05-30 12:11:09 -0700547 if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled
Selim Cinek547a06b2014-11-27 14:06:04 +0100548 || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200549 return false;
550 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100551
552 /*
Chris Wren5d53df42015-06-26 11:26:03 -0400553 * If the user drags anywhere inside the panel we intercept it if the movement is
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100554 * upwards. This allows closing the shade from anywhere inside the panel.
555 *
556 * We only do this if the current content is scrolled to the bottom,
557 * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture
558 * possible.
559 */
560 int pointerIndex = event.findPointerIndex(mTrackingPointer);
561 if (pointerIndex < 0) {
562 pointerIndex = 0;
563 mTrackingPointer = event.getPointerId(pointerIndex);
564 }
Jorim Jaggia6310292014-04-16 14:11:52 +0200565 final float x = event.getX(pointerIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100566 final float y = event.getY(pointerIndex);
567 boolean scrolledToBottom = isScrolledToBottom();
568
569 switch (event.getActionMasked()) {
570 case MotionEvent.ACTION_DOWN:
Jorim Jaggib690f0d2014-07-03 23:25:44 +0200571 mStatusBar.userActivity();
Selim Cinek173f2d02015-04-29 15:36:56 -0700572 mAnimatingOnDown = mHeightAnimator != null;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800573 mMinExpandHeight = 0.0f;
574 mDownTime = SystemClock.uptimeMillis();
575 if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
576 || mPeekAnimator != null) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200577 cancelHeightAnimator();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200578 cancelPeek();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +0200579 mTouchSlopExceeded = true;
Selim Cinek172e9142014-05-07 19:38:00 +0200580 return true;
581 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100582 mInitialTouchY = y;
Jorim Jaggia6310292014-04-16 14:11:52 +0200583 mInitialTouchX = x;
Selim Cinekab1dc952014-10-30 20:20:29 +0100584 mTouchStartedInEmptyArea = !isInContentBounds(x, y);
Selim Cinek08bd435a2019-07-26 11:37:14 -0700585 mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
Jorim Jaggi3857ac42014-06-27 18:01:12 +0200586 mJustPeeked = false;
Selim Cinek547a06b2014-11-27 14:06:04 +0100587 mMotionAborted = false;
Selim Cinek684a4422015-04-15 16:18:39 -0700588 mPanelClosedOnDown = isFullyCollapsed();
Selim Cinek79d79c42015-05-21 16:14:45 -0700589 mCollapsedAndHeadsUpOnDown = false;
Selim Cinek31094df2014-08-14 19:28:15 +0200590 mHasLayoutedSinceDown = false;
591 mUpdateFlingOnLayout = false;
Selim Cinek19c8c702014-08-25 22:09:19 +0200592 mTouchAboveFalsingThreshold = false;
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700593 addMovement(event);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100594 break;
595 case MotionEvent.ACTION_POINTER_UP:
596 final int upPointer = event.getPointerId(event.getActionIndex());
597 if (mTrackingPointer == upPointer) {
598 // gesture is ongoing, find a new pointer to track
599 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
600 mTrackingPointer = event.getPointerId(newIndex);
Jorim Jaggia6310292014-04-16 14:11:52 +0200601 mInitialTouchX = event.getX(newIndex);
602 mInitialTouchY = event.getY(newIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100603 }
604 break;
Selim Cinek547a06b2014-11-27 14:06:04 +0100605 case MotionEvent.ACTION_POINTER_DOWN:
Jason Monk1fd3fc32018-08-14 17:20:09 -0400606 if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
Selim Cinek547a06b2014-11-27 14:06:04 +0100607 mMotionAborted = true;
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700608 mVelocityTracker.clear();
Selim Cinek547a06b2014-11-27 14:06:04 +0100609 }
610 break;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100611 case MotionEvent.ACTION_MOVE:
612 final float h = y - mInitialTouchY;
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700613 addMovement(event);
Selim Cinek173f2d02015-04-29 15:36:56 -0700614 if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
615 float hAbs = Math.abs(h);
616 if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
617 && hAbs > Math.abs(x - mInitialTouchX)) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200618 cancelHeightAnimator();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700619 startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100620 return true;
621 }
622 }
623 break;
Selim Cinek31094df2014-08-14 19:28:15 +0200624 case MotionEvent.ACTION_CANCEL:
625 case MotionEvent.ACTION_UP:
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700626 mVelocityTracker.clear();
Selim Cinek31094df2014-08-14 19:28:15 +0200627 break;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100628 }
629 return false;
630 }
631
Selim Cinekab1dc952014-10-30 20:20:29 +0100632 /**
633 * @return Whether a pair of coordinates are inside the visible view content bounds.
634 */
635 protected abstract boolean isInContentBounds(float x, float y);
636
Selim Cinek831941f2015-06-17 15:09:30 -0700637 protected void cancelHeightAnimator() {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200638 if (mHeightAnimator != null) {
Selim Cinekd18b86f2017-06-21 09:44:52 -0700639 if (mHeightAnimator.isRunning()) {
640 mPanelUpdateWhenAnimatorEnds = false;
641 }
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200642 mHeightAnimator.cancel();
643 }
644 endClosing();
645 }
646
647 private void endClosing() {
648 if (mClosing) {
649 mClosing = false;
650 onClosingFinished();
651 }
652 }
653
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100654 protected boolean isScrolledToBottom() {
Selim Cinek172e9142014-05-07 19:38:00 +0200655 return true;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100656 }
657
658 protected float getContentHeight() {
659 return mExpandedHeight;
Daniel Sandlerbf526d12012-09-04 22:56:44 -0400660 }
661
Daniel Sandler08d05e32012-08-08 16:39:54 -0400662 @Override
663 protected void onFinishInflate() {
664 super.onFinishInflate();
665 loadDimens();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400666 }
667
Jorim Jaggi069cd032014-05-15 03:09:01 +0200668 @Override
669 protected void onConfigurationChanged(Configuration newConfig) {
670 super.onConfigurationChanged(newConfig);
671 loadDimens();
Jorim Jaggi069cd032014-05-15 03:09:01 +0200672 }
673
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200674 /**
Jorim Jaggib7240132014-06-30 01:39:07 +0200675 * @param vel the current vertical velocity of the motion
676 * @param vectorVel the length of the vectorial velocity
Jorim Jaggie29b2db2014-05-30 23:17:03 +0200677 * @return whether a fling should expands the panel; contracts otherwise
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200678 */
Selim Cinek9db71052015-04-24 18:54:30 -0700679 protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
Dave Mankoffc88d6222018-10-25 15:31:20 -0400680 if (mFalsingManager.isUnlockingDisabled()) {
681 return true;
682 }
683
Selim Cinek9db71052015-04-24 18:54:30 -0700684 if (isFalseTouch(x, y)) {
Selim Cinek19c8c702014-08-25 22:09:19 +0200685 return true;
686 }
Jorim Jaggib7240132014-06-30 01:39:07 +0200687 if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
Selim Cinek08bd435a2019-07-26 11:37:14 -0700688 return shouldExpandWhenNotFlinging();
Selim Cinek1685e632014-04-08 02:27:49 +0200689 } else {
Jorim Jaggie29b2db2014-05-30 23:17:03 +0200690 return vel > 0;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400691 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200692 }
693
Selim Cinek08bd435a2019-07-26 11:37:14 -0700694 protected boolean shouldExpandWhenNotFlinging() {
695 return getExpandedFraction() > 0.5f;
696 }
697
Selim Cinek9db71052015-04-24 18:54:30 -0700698 /**
699 * @param x the final x-coordinate when the finger was lifted
700 * @param y the final y-coordinate when the finger was lifted
701 * @return whether this motion should be regarded as a false touch
702 */
703 private boolean isFalseTouch(float x, float y) {
704 if (!mStatusBar.isFalsingThresholdNeeded()) {
705 return false;
706 }
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700707 if (mFalsingManager.isClassiferEnabled()) {
708 return mFalsingManager.isFalseTouch();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700709 }
Selim Cinek9db71052015-04-24 18:54:30 -0700710 if (!mTouchAboveFalsingThreshold) {
711 return true;
712 }
713 if (mUpwardsWhenTresholdReached) {
714 return false;
715 }
716 return !isDirectionUpwards(x, y);
Selim Cinek5386fb32014-09-03 16:37:36 +0200717 }
718
Jorim Jaggi1d480692014-05-20 19:41:58 +0200719 protected void fling(float vel, boolean expand) {
Selim Cinek9db71052015-04-24 18:54:30 -0700720 fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700721 }
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200722
Selim Cinek9db71052015-04-24 18:54:30 -0700723 protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
724 fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
725 }
726
727 protected void fling(float vel, boolean expand, float collapseSpeedUpFactor,
728 boolean expandBecauseOfFalsing) {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700729 cancelPeek();
Selim Cinek48ff9b42016-11-09 19:31:51 -0800730 float target = expand ? getMaxPanelHeight() : 0;
Jorim Jaggi3e02adb2015-05-13 17:50:26 -0700731 if (!expand) {
732 mClosing = true;
733 }
Selim Cinek9db71052015-04-24 18:54:30 -0700734 flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700735 }
736
737 protected void flingToHeight(float vel, boolean expand, float target,
Selim Cinek9db71052015-04-24 18:54:30 -0700738 float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200739 // Hack to make the expand transition look nice when clear all button is visible - we make
740 // the animation only to the last notification, and then jump to the maximum panel height so
741 // clear all just fades in and the decelerating motion is towards the last notification.
742 final boolean clearAllExpandHack = expand && fullyExpandedClearAllVisible()
743 && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
744 && !isClearAllVisible();
745 if (clearAllExpandHack) {
746 target = getMaxPanelHeight() - getClearAllHeight();
747 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200748 if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200749 notifyExpandingFinished();
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200750 return;
751 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200752 mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
Jorim Jaggi90129582014-06-02 14:44:49 +0200753 ValueAnimator animator = createHeightAnimator(target);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200754 if (expand) {
Selim Cinek61190fb2016-12-07 12:16:45 -0800755 if (expandBecauseOfFalsing && vel < 0) {
Selim Cinek5386fb32014-09-03 16:37:36 +0200756 vel = 0;
757 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200758 mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
Jorim Jaggi6626f542016-08-22 13:08:44 -0700759 if (vel == 0) {
Selim Cinek5386fb32014-09-03 16:37:36 +0200760 animator.setDuration(350);
761 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200762 } else {
Selim Cinek2411f762016-12-28 17:05:08 +0100763 if (shouldUseDismissingAnimation()) {
Selim Cinekdf5501b2017-04-14 20:01:27 -0700764 if (vel == 0) {
765 animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
766 long duration = (long) (200 + mExpandedHeight / getHeight() * 100);
767 animator.setDuration(duration);
768 } else {
769 mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
770 getHeight());
771 }
Selim Cinek2411f762016-12-28 17:05:08 +0100772 } else {
773 mFlingAnimationUtilsClosing
774 .apply(animator, mExpandedHeight, target, vel, getHeight());
775 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200776
777 // Make it shorter if we run a canned animation
778 if (vel == 0) {
Selim Cinek593ee202016-12-28 15:01:16 +0100779 animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
Jorim Jaggi1d480692014-05-20 19:41:58 +0200780 }
Selim Cinek2627d722018-01-19 12:16:49 -0800781 if (mFixedDuration != NO_FIXED_DURATION) {
782 animator.setDuration(mFixedDuration);
783 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200784 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200785 animator.addListener(new AnimatorListenerAdapter() {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200786 private boolean mCancelled;
787
788 @Override
789 public void onAnimationCancel(Animator animation) {
790 mCancelled = true;
791 }
792
Jorim Jaggi1d480692014-05-20 19:41:58 +0200793 @Override
794 public void onAnimationEnd(Animator animation) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200795 if (clearAllExpandHack && !mCancelled) {
Jorim Jaggi2ae259d2014-08-04 23:35:47 +0200796 setExpandedHeightInternal(getMaxPanelHeight());
797 }
Selim Cinekbe2c4432017-05-30 12:11:09 -0700798 setAnimator(null);
Jorim Jaggi2ae259d2014-08-04 23:35:47 +0200799 if (!mCancelled) {
800 notifyExpandingFinished();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200801 }
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700802 notifyBarPanelExpansionChanged();
Jorim Jaggi1d480692014-05-20 19:41:58 +0200803 }
804 });
Selim Cinekbe2c4432017-05-30 12:11:09 -0700805 setAnimator(animator);
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200806 animator.start();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400807 }
808
Selim Cinek2411f762016-12-28 17:05:08 +0100809 protected abstract boolean shouldUseDismissingAnimation();
810
Daniel Sandler08d05e32012-08-08 16:39:54 -0400811 @Override
812 protected void onAttachedToWindow() {
813 super.onAttachedToWindow();
Daniel Sandler978f8532012-08-15 15:48:16 -0400814 mViewName = getResources().getResourceName(getId());
815 }
816
817 public String getName() {
818 return mViewName;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400819 }
820
Daniel Sandler08d05e32012-08-08 16:39:54 -0400821 public void setExpandedHeight(float height) {
John Spurlock97642182013-07-29 17:58:39 -0400822 if (DEBUG) logf("setExpandedHeight(%.1f)", height);
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200823 setExpandedHeightInternal(height + getOverExpansionPixels());
Daniel Sandler08d05e32012-08-08 16:39:54 -0400824 }
825
Daniel Sandler50508132012-08-16 14:10:53 -0400826 @Override
Lucas Dupin60661a62018-04-12 10:50:13 -0700827 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Daniel Sandler50508132012-08-16 14:10:53 -0400828 super.onLayout(changed, left, top, right, bottom);
Selim Cinek5fbc6322016-01-15 17:17:58 -0800829 mStatusBar.onPanelLaidOut();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100830 requestPanelHeightUpdate();
Selim Cinek31094df2014-08-14 19:28:15 +0200831 mHasLayoutedSinceDown = true;
832 if (mUpdateFlingOnLayout) {
833 abortAnimations();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700834 fling(mUpdateFlingVelocity, true /* expands */);
Selim Cinek31094df2014-08-14 19:28:15 +0200835 mUpdateFlingOnLayout = false;
836 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100837 }
838
839 protected void requestPanelHeightUpdate() {
840 float currentMaxPanelHeight = getMaxPanelHeight();
841
Selim Cinekbe2c4432017-05-30 12:11:09 -0700842 if (isFullyCollapsed()) {
843 return;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100844 }
Selim Cinekbe2c4432017-05-30 12:11:09 -0700845
846 if (currentMaxPanelHeight == mExpandedHeight) {
847 return;
848 }
849
850 if (mPeekAnimator != null || mPeekTouching) {
851 return;
852 }
853
854 if (mTracking && !isTrackingBlocked()) {
855 return;
856 }
857
858 if (mHeightAnimator != null) {
859 mPanelUpdateWhenAnimatorEnds = true;
860 return;
861 }
862
863 setExpandedHeight(currentMaxPanelHeight);
Daniel Sandler50508132012-08-16 14:10:53 -0400864 }
865
Daniel Sandler08d05e32012-08-08 16:39:54 -0400866 public void setExpandedHeightInternal(float h) {
Jorim Jaggie05256e2016-09-08 14:58:20 -0700867 if (mExpandLatencyTracking && h != 0f) {
868 DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(mContext).onActionEnd(
869 LatencyTracker.ACTION_EXPAND_PANEL));
870 mExpandLatencyTracking = false;
871 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200872 float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
873 if (mHeightAnimator == null) {
874 float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
875 if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
876 setOverExpansion(overExpansionPixels, true /* isPixels */);
877 }
878 mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
879 } else {
880 mExpandedHeight = h;
881 if (mOverExpandedBeforeFling) {
882 setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
883 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100884 }
Daniel Sandler198a0302012-08-17 16:04:31 -0400885
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200886 // If we are closing the panel and we are almost there due to a slow decelerating
887 // interpolator, abort the animation.
888 if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
889 mExpandedHeight = 0f;
890 if (mHeightAnimator != null) {
891 mHeightAnimator.end();
892 }
893 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800894 mExpandedFraction = Math.min(1f,
895 fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100896 onHeightUpdated(mExpandedHeight);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200897 notifyBarPanelExpansionChanged();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400898 }
899
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200900 /**
901 * @return true if the panel tracking should be temporarily blocked; this is used when a
902 * conflicting gesture (opening QS) is happening
903 */
904 protected abstract boolean isTrackingBlocked();
905
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200906 protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
Selim Cinek24120a52014-05-26 10:05:42 +0200907
Jorim Jaggi90129582014-06-02 14:44:49 +0200908 protected abstract void onHeightUpdated(float expandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100909
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200910 protected abstract float getOverExpansionAmount();
911
912 protected abstract float getOverExpansionPixels();
913
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100914 /**
915 * This returns the maximum height of the panel. Children should override this if their
916 * desired height is not the full height.
917 *
918 * @return the default implementation simply returns the maximum height.
919 */
Selim Cinek31094df2014-08-14 19:28:15 +0200920 protected abstract int getMaxPanelHeight();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400921
922 public void setExpandedFraction(float frac) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100923 setExpandedHeight(getMaxPanelHeight() * frac);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400924 }
925
926 public float getExpandedHeight() {
927 return mExpandedHeight;
928 }
929
930 public float getExpandedFraction() {
931 return mExpandedFraction;
932 }
933
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700934 public boolean isFullyExpanded() {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100935 return mExpandedHeight >= getMaxPanelHeight();
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700936 }
937
938 public boolean isFullyCollapsed() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800939 return mExpandedFraction <= 0.0f;
Daniel Sandler67eab792012-10-02 17:08:23 -0400940 }
941
942 public boolean isCollapsing() {
Selim Cinek7e222c3c2018-01-25 12:22:41 -0800943 return mClosing || mLaunchingNotification;
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700944 }
945
John Spurlocka4b70af2013-08-17 14:05:49 -0400946 public boolean isTracking() {
947 return mTracking;
948 }
949
Daniel Sandler08d05e32012-08-08 16:39:54 -0400950 public void setBar(PanelBar panelBar) {
951 mBar = panelBar;
952 }
953
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700954 public void collapse(boolean delayed, float speedUpFactor) {
John Spurlock97642182013-07-29 17:58:39 -0400955 if (DEBUG) logf("collapse: " + this);
Selim Cinek9bb05632016-12-15 14:14:29 -0800956 if (canPanelBeCollapsed()) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200957 cancelHeightAnimator();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200958 notifyExpandingStarted();
Jorim Jaggi3b866be2015-06-30 11:31:10 -0700959
960 // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
961 mClosing = true;
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200962 if (delayed) {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700963 mNextCollapseSpeedUpFactor = speedUpFactor;
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200964 postDelayed(mFlingCollapseRunnable, 120);
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200965 } else {
Selim Cinek9db71052015-04-24 18:54:30 -0700966 fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200967 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400968 }
969 }
970
Selim Cinek9bb05632016-12-15 14:14:29 -0800971 public boolean canPanelBeCollapsed() {
972 return !isFullyCollapsed() && !mTracking && !mClosing;
973 }
974
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200975 private final Runnable mFlingCollapseRunnable = new Runnable() {
976 @Override
977 public void run() {
Selim Cinek9db71052015-04-24 18:54:30 -0700978 fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
979 false /* expandBecauseOfFalsing */);
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200980 }
981 };
982
John Spurlock97642182013-07-29 17:58:39 -0400983 public void cancelPeek() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800984 boolean cancelled = false;
Jorim Jaggib472b3472014-06-30 19:56:24 +0200985 if (mPeekAnimator != null) {
Selim Cinek529c5322016-04-06 20:03:45 -0700986 cancelled = true;
John Spurlock97642182013-07-29 17:58:39 -0400987 mPeekAnimator.cancel();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400988 }
Jorim Jaggib472b3472014-06-30 19:56:24 +0200989
Selim Cinek529c5322016-04-06 20:03:45 -0700990 if (cancelled) {
991 // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
992 // notify mBar that we might have closed ourselves.
993 notifyBarPanelExpansionChanged();
994 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400995 }
Daniel Sandler37a38aa2013-02-13 17:15:57 -0500996
Oren Blasberg8d3fea12015-07-10 14:21:44 -0700997 public void expand(final boolean animate) {
998 if (!isFullyCollapsed() && !isCollapsing()) {
999 return;
1000 }
1001
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001002 mInstantExpanding = true;
Jorim Jaggi6626f542016-08-22 13:08:44 -07001003 mAnimateAfterExpanding = animate;
Jorim Jaggi5cbfe542014-09-10 22:23:08 +02001004 mUpdateFlingOnLayout = false;
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001005 abortAnimations();
Jorim Jaggi5cbfe542014-09-10 22:23:08 +02001006 cancelPeek();
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001007 if (mTracking) {
1008 onTrackingStopped(true /* expands */); // The panel is expanded after this call.
Jorim Jaggie62d5892014-09-02 16:31:19 +02001009 }
1010 if (mExpanding) {
Jorim Jaggib472b3472014-06-30 19:56:24 +02001011 notifyExpandingFinished();
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001012 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001013 notifyBarPanelExpansionChanged();
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001014
1015 // Wait for window manager to pickup the change, so we know the maximum height of the panel
1016 // then.
1017 getViewTreeObserver().addOnGlobalLayoutListener(
1018 new ViewTreeObserver.OnGlobalLayoutListener() {
1019 @Override
1020 public void onGlobalLayout() {
Jorim Jaggic5804af2016-04-25 18:51:16 -07001021 if (!mInstantExpanding) {
1022 getViewTreeObserver().removeOnGlobalLayoutListener(this);
1023 return;
1024 }
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001025 if (mStatusBar.getStatusBarWindow().getHeight()
1026 != mStatusBar.getStatusBarHeight()) {
1027 getViewTreeObserver().removeOnGlobalLayoutListener(this);
Jorim Jaggi6626f542016-08-22 13:08:44 -07001028 if (mAnimateAfterExpanding) {
Oren Blasberg8d3fea12015-07-10 14:21:44 -07001029 notifyExpandingStarted();
1030 fling(0, true /* expand */);
1031 } else {
1032 setExpandedFraction(1f);
1033 }
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001034 mInstantExpanding = false;
1035 }
1036 }
1037 });
1038
1039 // Make sure a layout really happens.
1040 requestLayout();
1041 }
1042
Selim Cinek6bb4a9b2014-10-09 17:48:05 -07001043 public void instantCollapse() {
1044 abortAnimations();
1045 setExpandedFraction(0f);
1046 if (mExpanding) {
1047 notifyExpandingFinished();
1048 }
Jorim Jaggic5804af2016-04-25 18:51:16 -07001049 if (mInstantExpanding) {
1050 mInstantExpanding = false;
1051 notifyBarPanelExpansionChanged();
1052 }
Selim Cinek6bb4a9b2014-10-09 17:48:05 -07001053 }
1054
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001055 private void abortAnimations() {
1056 cancelPeek();
Selim Cinekdbbcfbe2014-10-24 17:52:35 +02001057 cancelHeightAnimator();
Jorim Jaggi5cbfe542014-09-10 22:23:08 +02001058 removeCallbacks(mPostCollapseRunnable);
1059 removeCallbacks(mFlingCollapseRunnable);
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001060 }
1061
Selim Cinekdbbcfbe2014-10-24 17:52:35 +02001062 protected void onClosingFinished() {
1063 mBar.onClosingFinished();
1064 }
1065
1066
Jorim Jaggi90129582014-06-02 14:44:49 +02001067 protected void startUnlockHintAnimation() {
1068
1069 // We don't need to hint the user if an animation is already running or the user is changing
1070 // the expansion.
1071 if (mHeightAnimator != null || mTracking) {
1072 return;
1073 }
1074 cancelPeek();
Jorim Jaggib472b3472014-06-30 19:56:24 +02001075 notifyExpandingStarted();
Lucas Dupin60661a62018-04-12 10:50:13 -07001076 startUnlockHintAnimationPhase1(() -> {
1077 notifyExpandingFinished();
1078 onUnlockHintFinished();
1079 mHintAnimationRunning = false;
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001080 });
Selim Cinekec29d342017-05-05 18:31:49 -07001081 onUnlockHintStarted();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001082 mHintAnimationRunning = true;
Jorim Jaggi90129582014-06-02 14:44:49 +02001083 }
1084
Selim Cinekec29d342017-05-05 18:31:49 -07001085 protected void onUnlockHintFinished() {
1086 mStatusBar.onHintFinished();
1087 }
1088
1089 protected void onUnlockHintStarted() {
1090 mStatusBar.onUnlockHintStarted();
1091 }
1092
Lucas Dupinbc9aac12018-03-04 20:18:15 -08001093 public boolean isUnlockHintRunning() {
1094 return mHintAnimationRunning;
1095 }
1096
Jorim Jaggi90129582014-06-02 14:44:49 +02001097 /**
1098 * Phase 1: Move everything upwards.
1099 */
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001100 private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
Jorim Jaggi90129582014-06-02 14:44:49 +02001101 float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
1102 ValueAnimator animator = createHeightAnimator(target);
1103 animator.setDuration(250);
Selim Cinekc18010f2016-01-20 13:41:30 -08001104 animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
Jorim Jaggi90129582014-06-02 14:44:49 +02001105 animator.addListener(new AnimatorListenerAdapter() {
1106 private boolean mCancelled;
1107
1108 @Override
1109 public void onAnimationCancel(Animator animation) {
1110 mCancelled = true;
1111 }
1112
1113 @Override
1114 public void onAnimationEnd(Animator animation) {
1115 if (mCancelled) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07001116 setAnimator(null);
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001117 onAnimationFinished.run();
Jorim Jaggi90129582014-06-02 14:44:49 +02001118 } else {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001119 startUnlockHintAnimationPhase2(onAnimationFinished);
Jorim Jaggi90129582014-06-02 14:44:49 +02001120 }
1121 }
1122 });
1123 animator.start();
Selim Cinekbe2c4432017-05-30 12:11:09 -07001124 setAnimator(animator);
Adrian Roos8d4e99f2017-05-25 18:03:58 -07001125
1126 View[] viewsToAnimate = {
1127 mKeyguardBottomArea.getIndicationArea(),
1128 mStatusBar.getAmbientIndicationContainer()};
1129 for (View v : viewsToAnimate) {
1130 if (v == null) {
1131 continue;
1132 }
1133 v.animate()
1134 .translationY(-mHintDistance)
1135 .setDuration(250)
1136 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
1137 .withEndAction(() -> v.animate()
1138 .translationY(0)
1139 .setDuration(450)
1140 .setInterpolator(mBounceInterpolator)
1141 .start())
1142 .start();
1143 }
Jorim Jaggi90129582014-06-02 14:44:49 +02001144 }
1145
Selim Cinekbe2c4432017-05-30 12:11:09 -07001146 private void setAnimator(ValueAnimator animator) {
1147 mHeightAnimator = animator;
1148 if (animator == null && mPanelUpdateWhenAnimatorEnds) {
1149 mPanelUpdateWhenAnimatorEnds = false;
1150 requestPanelHeightUpdate();
1151 }
1152 }
1153
Jorim Jaggi90129582014-06-02 14:44:49 +02001154 /**
1155 * Phase 2: Bounce down.
1156 */
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001157 private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
Jorim Jaggi90129582014-06-02 14:44:49 +02001158 ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
1159 animator.setDuration(450);
1160 animator.setInterpolator(mBounceInterpolator);
1161 animator.addListener(new AnimatorListenerAdapter() {
1162 @Override
1163 public void onAnimationEnd(Animator animation) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07001164 setAnimator(null);
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001165 onAnimationFinished.run();
Jorim Jaggieb8f11a2015-05-20 18:42:16 -07001166 notifyBarPanelExpansionChanged();
Jorim Jaggi90129582014-06-02 14:44:49 +02001167 }
1168 });
1169 animator.start();
Selim Cinekbe2c4432017-05-30 12:11:09 -07001170 setAnimator(animator);
Jorim Jaggi90129582014-06-02 14:44:49 +02001171 }
1172
1173 private ValueAnimator createHeightAnimator(float targetHeight) {
1174 ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
Lucas Dupin2e838ac2019-04-17 16:50:58 -07001175 animator.addUpdateListener(
1176 animation -> setExpandedHeightInternal((float) animation.getAnimatedValue()));
Jorim Jaggi90129582014-06-02 14:44:49 +02001177 return animator;
1178 }
1179
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001180 protected void notifyBarPanelExpansionChanged() {
Evan Laird0de26b02019-03-01 15:12:50 -05001181 if (mBar != null) {
Aran Inkb6aadb42019-05-16 14:22:12 -04001182 mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f
Evan Laird0de26b02019-03-01 15:12:50 -05001183 || mPeekAnimator != null || mInstantExpanding
1184 || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null);
1185 }
Selim Cineka678b0e2019-07-23 13:12:55 -07001186 for (int i = 0; i < mExpansionListeners.size(); i++) {
1187 mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
Lucas Dupinbc9aac12018-03-04 20:18:15 -08001188 }
1189 }
1190
Selim Cineka678b0e2019-07-23 13:12:55 -07001191 public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
1192 mExpansionListeners.add(panelExpansionListener);
Jorim Jaggib472b3472014-06-30 19:56:24 +02001193 }
1194
Selim Cinek0fccc722015-07-29 17:04:36 -07001195 protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
1196
Jorim Jaggi90129582014-06-02 14:44:49 +02001197 /**
1198 * Gets called when the user performs a click anywhere in the empty area of the panel.
1199 *
1200 * @return whether the panel will be expanded after the action performed by this method
1201 */
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001202 protected boolean onEmptySpaceClick(float x) {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001203 if (mHintAnimationRunning) {
1204 return true;
1205 }
Selim Cinek6746c282015-04-21 19:58:31 -07001206 return onMiddleClicked();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001207 }
1208
Jorim Jaggi1cf6e102015-01-20 16:45:05 +01001209 protected final Runnable mPostCollapseRunnable = new Runnable() {
Jorim Jaggi488b7922014-08-05 21:12:02 +02001210 @Override
1211 public void run() {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -07001212 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
Jorim Jaggi488b7922014-08-05 21:12:02 +02001213 }
1214 };
Jorim Jaggi488b7922014-08-05 21:12:02 +02001215
Jorim Jaggi1cf6e102015-01-20 16:45:05 +01001216 protected abstract boolean onMiddleClicked();
Jorim Jaggi90129582014-06-02 14:44:49 +02001217
Jorim Jaggid7912d22014-09-30 17:38:19 +02001218 protected abstract boolean isDozing();
1219
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001220 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Selim Cineka3e5bcb2014-04-07 20:07:22 +02001221 pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
Jorim Jaggi50d87a72014-09-12 21:43:35 +02001222 + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s touchDisabled=%s"
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001223 + "]",
1224 this.getClass().getSimpleName(),
1225 getExpandedHeight(),
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001226 getMaxPanelHeight(),
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001227 mClosing?"T":"f",
1228 mTracking?"T":"f",
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001229 mJustPeeked?"T":"f",
1230 mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""),
Jorim Jaggi50d87a72014-09-12 21:43:35 +02001231 mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":""),
1232 mTouchDisabled?"T":"f"
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001233 ));
1234 }
Selim Cinek3c4635c2014-05-29 02:12:47 +02001235
Lucas Dupinee63c372018-08-03 11:58:24 -07001236 public abstract void resetViews(boolean animate);
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001237
1238 protected abstract float getPeekHeight();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02001239 /**
1240 * @return whether "Clear all" button will be visible when the panel is fully expanded
1241 */
1242 protected abstract boolean fullyExpandedClearAllVisible();
1243
1244 protected abstract boolean isClearAllVisible();
1245
1246 /**
1247 * @return the height of the clear all button, in pixels
1248 */
1249 protected abstract int getClearAllHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001250
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001251 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001252 mHeadsUpManager = headsUpManager;
1253 }
Selim Cinek2627d722018-01-19 12:16:49 -08001254
1255 public void setLaunchingNotification(boolean launchingNotification) {
1256 mLaunchingNotification = launchingNotification;
1257 }
1258
1259 public void collapseWithDuration(int animationDuration) {
1260 mFixedDuration = animationDuration;
1261 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
1262 mFixedDuration = NO_FIXED_DURATION;
1263 }
Daniel Sandler08d05e32012-08-08 16:39:54 -04001264}