blob: a9a3b2d866e1360595f5df6b22c456d62c572d97 [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;
Lucas Dupin2e838ac2019-04-17 16:50:58 -070053import com.android.systemui.statusbar.phone.NotificationPanelView.PanelExpansionListener;
Jason Monk1fd3fc32018-08-14 17:20:09 -040054import com.android.systemui.statusbar.policy.KeyguardMonitor;
Daniel Sandler08d05e32012-08-08 16:39:54 -040055
John Spurlockde84f0e2013-06-12 12:41:00 -040056import java.io.FileDescriptor;
57import java.io.PrintWriter;
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 Cinek48ff9b42016-11-09 19:31:51 -080065 private long mDownTime;
66 private float mMinExpandHeight;
Chris Wren27a52fa2017-02-01 14:21:43 -050067 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
Selim Cinekbe2c4432017-05-30 12:11:09 -070068 private boolean mPanelUpdateWhenAnimatorEnds;
Selim Cinekf6559f82017-08-16 14:49:10 -070069 private boolean mVibrateOnOpening;
Selim Cinek2627d722018-01-19 12:16:49 -080070 protected boolean mLaunchingNotification;
71 private int mFixedDuration = NO_FIXED_DURATION;
Lucas Dupin2e838ac2019-04-17 16:50:58 -070072 protected PanelExpansionListener mExpansionListener;
Selim Cinek173f2d02015-04-29 15:36:56 -070073
John Spurlock97642182013-07-29 17:58:39 -040074 private final void logf(String fmt, Object... args) {
John Spurlockcd686b52013-06-05 10:13:46 -040075 Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
Daniel Sandler08d05e32012-08-08 16:39:54 -040076 }
77
Jason Monk2a6ea9c2017-01-26 11:14:51 -050078 protected StatusBar mStatusBar;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090079 protected HeadsUpManagerPhone mHeadsUpManager;
Selim Cinek684a4422015-04-15 16:18:39 -070080
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040081 private float mPeekHeight;
Jorim Jaggi90129582014-06-02 14:44:49 +020082 private float mHintDistance;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010083 private float mInitialOffsetOnTouch;
Selim Cinek79d79c42015-05-21 16:14:45 -070084 private boolean mCollapsedAndHeadsUpOnDown;
Daniel Sandler08d05e32012-08-08 16:39:54 -040085 private float mExpandedFraction = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +020086 protected float mExpandedHeight = 0;
Selim Cinek31094df2014-08-14 19:28:15 +020087 private boolean mPanelClosedOnDown;
88 private boolean mHasLayoutedSinceDown;
89 private float mUpdateFlingVelocity;
90 private boolean mUpdateFlingOnLayout;
Jorim Jaggib7a33032014-08-20 16:21:36 +020091 private boolean mPeekTouching;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040092 private boolean mJustPeeked;
Daniel Sandler50508132012-08-16 14:10:53 -040093 private boolean mClosing;
Jorim Jaggi8dd95e02014-06-03 16:19:33 +020094 protected boolean mTracking;
Jorim Jaggi90129582014-06-02 14:44:49 +020095 private boolean mTouchSlopExceeded;
John Spurlock48fa91a2013-08-15 09:29:31 -040096 private int mTrackingPointer;
Jorim Jaggid7daab72014-05-06 22:22:20 +020097 protected int mTouchSlop;
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +020098 protected boolean mHintAnimationRunning;
Jorim Jaggi47c85a32014-06-05 17:25:40 +020099 private boolean mOverExpandedBeforeFling;
Selim Cinek19c8c702014-08-25 22:09:19 +0200100 private boolean mTouchAboveFalsingThreshold;
101 private int mUnlockFalsingThreshold;
Selim Cinekab1dc952014-10-30 20:20:29 +0100102 private boolean mTouchStartedInEmptyArea;
Selim Cinek547a06b2014-11-27 14:06:04 +0100103 private boolean mMotionAborted;
Selim Cinek9db71052015-04-24 18:54:30 -0700104 private boolean mUpwardsWhenTresholdReached;
Selim Cinek173f2d02015-04-29 15:36:56 -0700105 private boolean mAnimatingOnDown;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400106
Jorim Jaggi1d480692014-05-20 19:41:58 +0200107 private ValueAnimator mHeightAnimator;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400108 private ObjectAnimator mPeekAnimator;
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700109 private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
Jorim Jaggi1d480692014-05-20 19:41:58 +0200110 private FlingAnimationUtils mFlingAnimationUtils;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800111 private FlingAnimationUtils mFlingAnimationUtilsClosing;
Selim Cinek2411f762016-12-28 17:05:08 +0100112 private FlingAnimationUtils mFlingAnimationUtilsDismissing;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700113 private FalsingManager mFalsingManager;
Matthew Ng5c0592e2018-03-08 14:51:36 -0800114 private final VibratorHelper mVibratorHelper;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400115
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200116 /**
117 * Whether an instant expand request is currently pending and we are just waiting for layout.
118 */
119 private boolean mInstantExpanding;
Jorim Jaggi6626f542016-08-22 13:08:44 -0700120 private boolean mAnimateAfterExpanding;
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200121
Daniel Sandler08d05e32012-08-08 16:39:54 -0400122 PanelBar mBar;
123
Daniel Sandler50508132012-08-16 14:10:53 -0400124 private String mViewName;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200125 private float mInitialTouchY;
126 private float mInitialTouchX;
Jorim Jaggid41083a2014-09-12 02:54:40 +0200127 private boolean mTouchDisabled;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400128
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800129 /**
130 * Whether or not the PanelView can be expanded or collapsed with a drag.
131 */
132 private boolean mNotificationsDragEnabled;
133
Jorim Jaggi90129582014-06-02 14:44:49 +0200134 private Interpolator mBounceInterpolator;
Selim Cinekf99d0002014-06-13 07:36:01 +0200135 protected KeyguardBottomAreaView mKeyguardBottomArea;
Jorim Jaggi90129582014-06-02 14:44:49 +0200136
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700137 /**
138 * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time.
139 */
140 private float mNextCollapseSpeedUpFactor = 1.0f;
141
Jorim Jaggi1d071ce2015-07-22 14:05:06 -0700142 protected boolean mExpanding;
Jorim Jaggib472b3472014-06-30 19:56:24 +0200143 private boolean mGestureWaitForTouchSlop;
Selim Cinek9db71052015-04-24 18:54:30 -0700144 private boolean mIgnoreXTouchSlop;
Jorim Jaggie05256e2016-09-08 14:58:20 -0700145 private boolean mExpandLatencyTracking;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400146 protected final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
Beverly8fdb5332019-02-04 14:29:49 -0500147 protected final SysuiStatusBarStateController mStatusBarStateController =
148 (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200149
Selim Cinek1685e632014-04-08 02:27:49 +0200150 protected void onExpandingFinished() {
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200151 mBar.onExpandingFinished();
Selim Cinek1685e632014-04-08 02:27:49 +0200152 }
153
154 protected void onExpandingStarted() {
155 }
156
Jorim Jaggib472b3472014-06-30 19:56:24 +0200157 private void notifyExpandingStarted() {
158 if (!mExpanding) {
159 mExpanding = true;
160 onExpandingStarted();
161 }
162 }
163
Jorim Jaggi1d071ce2015-07-22 14:05:06 -0700164 protected final void notifyExpandingFinished() {
Selim Cinekb0e4f9e2015-11-02 13:42:58 -0800165 endClosing();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200166 if (mExpanding) {
167 mExpanding = false;
168 onExpandingFinished();
169 }
170 }
171
Selim Cinek48ff9b42016-11-09 19:31:51 -0800172 private void runPeekAnimation(long duration, float peekHeight, boolean collapseWhenFinished) {
173 mPeekHeight = peekHeight;
John Spurlock97642182013-07-29 17:58:39 -0400174 if (DEBUG) logf("peek to height=%.1f", mPeekHeight);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200175 if (mHeightAnimator != null) {
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400176 return;
177 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800178 if (mPeekAnimator != null) {
179 mPeekAnimator.cancel();
180 }
Jorim Jaggib472b3472014-06-30 19:56:24 +0200181 mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight)
Selim Cinek48ff9b42016-11-09 19:31:51 -0800182 .setDuration(duration);
Selim Cinekc18010f2016-01-20 13:41:30 -0800183 mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200184 mPeekAnimator.addListener(new AnimatorListenerAdapter() {
185 private boolean mCancelled;
186
187 @Override
188 public void onAnimationCancel(Animator animation) {
189 mCancelled = true;
190 }
191
192 @Override
193 public void onAnimationEnd(Animator animation) {
194 mPeekAnimator = null;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800195 if (!mCancelled && collapseWhenFinished) {
Selim Cineka0f5c762015-06-22 14:44:46 -0400196 postOnAnimation(mPostCollapseRunnable);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200197 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800198
Jorim Jaggib472b3472014-06-30 19:56:24 +0200199 }
200 });
201 notifyExpandingStarted();
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400202 mPeekAnimator.start();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200203 mJustPeeked = true;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400204 }
205
Daniel Sandler08d05e32012-08-08 16:39:54 -0400206 public PanelView(Context context, AttributeSet attrs) {
207 super(context, attrs);
Selim Cinek593ee202016-12-28 15:01:16 +0100208 mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f /* maxLengthSeconds */,
209 0.6f /* speedUpFactor */);
210 mFlingAnimationUtilsClosing = new FlingAnimationUtils(context, 0.5f /* maxLengthSeconds */,
211 0.6f /* speedUpFactor */);
Selim Cinek2411f762016-12-28 17:05:08 +0100212 mFlingAnimationUtilsDismissing = new FlingAnimationUtils(context,
213 0.5f /* maxLengthSeconds */, 0.2f /* speedUpFactor */, 0.6f /* x2 */,
214 0.84f /* y2 */);
Jorim Jaggi90129582014-06-02 14:44:49 +0200215 mBounceInterpolator = new BounceInterpolator();
Dave Mankoffdde5ee62019-05-02 17:36:11 -0400216 mFalsingManager = FalsingManagerFactory.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800217 mNotificationsDragEnabled =
218 getResources().getBoolean(R.bool.config_enableNotificationShadeDrag);
Matthew Nge2b4d792018-03-09 13:42:18 -0800219 mVibratorHelper = Dependency.get(VibratorHelper.class);
Selim Cinekf6559f82017-08-16 14:49:10 -0700220 mVibrateOnOpening = mContext.getResources().getBoolean(
221 R.bool.config_vibrateOnIconAnimation);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400222 }
223
Jorim Jaggi069cd032014-05-15 03:09:01 +0200224 protected void loadDimens() {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400225 final Resources res = getContext().getResources();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100226 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
227 mTouchSlop = configuration.getScaledTouchSlop();
Jorim Jaggi90129582014-06-02 14:44:49 +0200228 mHintDistance = res.getDimension(R.dimen.hint_move_distance);
Selim Cinek19c8c702014-08-25 22:09:19 +0200229 mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400230 }
231
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700232 private void addMovement(MotionEvent event) {
233 // Add movement to velocity tracker using raw screen X and Y coordinates instead
234 // of window coordinates because the window frame may be moving at the same time.
235 float deltaX = event.getRawX() - event.getX();
236 float deltaY = event.getRawY() - event.getY();
237 event.offsetLocation(deltaX, deltaY);
238 mVelocityTracker.addMovement(event);
239 event.offsetLocation(-deltaX, -deltaY);
240 }
241
Jason Monke59dc402018-08-16 12:05:01 -0400242 public void setTouchAndAnimationDisabled(boolean disabled) {
Jorim Jaggid41083a2014-09-12 02:54:40 +0200243 mTouchDisabled = disabled;
Selim Cinek48a92a52017-05-02 20:02:30 -0700244 if (mTouchDisabled) {
245 cancelHeightAnimator();
246 if (mTracking) {
247 onTrackingStopped(true /* expanded */);
248 }
Selim Cinekeede5d72017-05-22 17:09:55 -0700249 notifyExpandingFinished();
Selim Cinek3127daf02016-07-22 18:04:23 -0700250 }
Jorim Jaggid41083a2014-09-12 02:54:40 +0200251 }
252
Jorim Jaggie05256e2016-09-08 14:58:20 -0700253 public void startExpandLatencyTracking() {
254 if (LatencyTracker.isEnabled(mContext)) {
255 LatencyTracker.getInstance(mContext).onActionStart(
256 LatencyTracker.ACTION_EXPAND_PANEL);
257 mExpandLatencyTracking = true;
258 }
259 }
260
Daniel Sandlerbf526d12012-09-04 22:56:44 -0400261 @Override
262 public boolean onTouchEvent(MotionEvent event) {
lumark44f9e5a2018-05-07 21:49:35 +0800263 if (mInstantExpanding
264 || (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL)
Selim Cinek547a06b2014-11-27 14:06:04 +0100265 || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200266 return false;
267 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100268
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800269 // If dragging should not expand the notifications shade, then return false.
270 if (!mNotificationsDragEnabled) {
271 if (mTracking) {
272 // Turn off tracking if it's on or the shade can get stuck in the down position.
273 onTrackingStopped(true /* expand */);
274 }
275 return false;
276 }
277
Jun Mukaicf9933c2015-07-08 17:34:26 -0700278 // On expanding, single mouse click expands the panel instead of dragging.
Jun Mukaic99243a2015-10-15 02:59:57 -0700279 if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
Jun Mukaicf9933c2015-07-08 17:34:26 -0700280 if (event.getAction() == MotionEvent.ACTION_UP) {
281 expand(true);
282 }
283 return true;
284 }
285
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100286 /*
287 * We capture touch events here and update the expand height here in case according to
288 * the users fingers. This also handles multi-touch.
289 *
Chris Wren5d53df42015-06-26 11:26:03 -0400290 * If the user just clicks shortly, we show a quick peek of the shade.
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100291 *
292 * Flinging is also enabled in order to open or close the shade.
293 */
294
295 int pointerIndex = event.findPointerIndex(mTrackingPointer);
296 if (pointerIndex < 0) {
297 pointerIndex = 0;
298 mTrackingPointer = event.getPointerId(pointerIndex);
299 }
Jorim Jaggia6310292014-04-16 14:11:52 +0200300 final float x = event.getX(pointerIndex);
Selim Cinek547a06b2014-11-27 14:06:04 +0100301 final float y = event.getY(pointerIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100302
Jorim Jaggib472b3472014-06-30 19:56:24 +0200303 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
Selim Cinek684a4422015-04-15 16:18:39 -0700304 mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures();
305 mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200306 }
Selim Cinek4c6969a2014-05-26 19:22:17 +0200307
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100308 switch (event.getActionMasked()) {
309 case MotionEvent.ACTION_DOWN:
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700310 startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
Jorim Jaggi3857ac42014-06-27 18:01:12 +0200311 mJustPeeked = false;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800312 mMinExpandHeight = 0.0f;
Selim Cinek684a4422015-04-15 16:18:39 -0700313 mPanelClosedOnDown = isFullyCollapsed();
Selim Cinek31094df2014-08-14 19:28:15 +0200314 mHasLayoutedSinceDown = false;
315 mUpdateFlingOnLayout = false;
Selim Cinek547a06b2014-11-27 14:06:04 +0100316 mMotionAborted = false;
Jorim Jaggib7a33032014-08-20 16:21:36 +0200317 mPeekTouching = mPanelClosedOnDown;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800318 mDownTime = SystemClock.uptimeMillis();
Selim Cinek19c8c702014-08-25 22:09:19 +0200319 mTouchAboveFalsingThreshold = false;
Selim Cinek79d79c42015-05-21 16:14:45 -0700320 mCollapsedAndHeadsUpOnDown = isFullyCollapsed()
321 && mHeadsUpManager.hasPinnedHeadsUp();
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700322 addMovement(event);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800323 if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)
324 || mPeekAnimator != null) {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200325 mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
Selim Cinek48ff9b42016-11-09 19:31:51 -0800326 || mPeekAnimator != null;
Selim Cinekd18b86f2017-06-21 09:44:52 -0700327 cancelHeightAnimator();
328 cancelPeek();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200329 onTrackingStarted();
Jorim Jaggi1d480692014-05-20 19:41:58 +0200330 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -0800331 if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
332 && !mStatusBar.isBouncerShowing()) {
Evan Laird404a85c2018-02-28 15:31:29 -0500333 startOpening(event);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100334 }
335 break;
336
337 case MotionEvent.ACTION_POINTER_UP:
338 final int upPointer = event.getPointerId(event.getActionIndex());
339 if (mTrackingPointer == upPointer) {
340 // gesture is ongoing, find a new pointer to track
341 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
342 final float newY = event.getY(newIndex);
Jorim Jaggia6310292014-04-16 14:11:52 +0200343 final float newX = event.getX(newIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100344 mTrackingPointer = event.getPointerId(newIndex);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700345 startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100346 }
347 break;
Selim Cinek547a06b2014-11-27 14:06:04 +0100348 case MotionEvent.ACTION_POINTER_DOWN:
Jason Monk1fd3fc32018-08-14 17:20:09 -0400349 if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
Selim Cinek547a06b2014-11-27 14:06:04 +0100350 mMotionAborted = true;
351 endMotionEvent(event, x, y, true /* forceCancel */);
352 return false;
353 }
354 break;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100355 case MotionEvent.ACTION_MOVE:
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700356 addMovement(event);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200357 float h = y - mInitialTouchY;
Jorim Jaggib7240132014-06-30 01:39:07 +0200358
359 // If the panel was collapsed when touching, we only need to check for the
360 // y-component of the gesture, as we have no conflicting horizontal gesture.
361 if (Math.abs(h) > mTouchSlop
362 && (Math.abs(h) > Math.abs(x - mInitialTouchX)
Selim Cinek48ff9b42016-11-09 19:31:51 -0800363 || mIgnoreXTouchSlop)) {
Jorim Jaggi90129582014-06-02 14:44:49 +0200364 mTouchSlopExceeded = true;
Selim Cinek79d79c42015-05-21 16:14:45 -0700365 if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
Jorim Jaggi0b1528a2014-10-28 22:47:46 +0100366 if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700367 startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200368 h = 0;
369 }
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200370 cancelHeightAnimator();
Jorim Jaggi90129582014-06-02 14:44:49 +0200371 onTrackingStarted();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200372 }
Selim Cinek4c6969a2014-05-26 19:22:17 +0200373 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800374 float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200375 if (newHeight > mPeekHeight) {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200376 if (mPeekAnimator != null) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100377 mPeekAnimator.cancel();
378 }
379 mJustPeeked = false;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800380 } else if (mPeekAnimator == null && mJustPeeked) {
381 // The initial peek has finished, but we haven't dragged as far yet, lets
382 // speed it up by starting at the peek height.
383 mInitialOffsetOnTouch = mExpandedHeight;
384 mInitialTouchY = y;
385 mMinExpandHeight = mExpandedHeight;
386 mJustPeeked = false;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100387 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800388 newHeight = Math.max(newHeight, mMinExpandHeight);
Selim Cinek29ed3c92014-09-23 20:44:35 +0200389 if (-h >= getFalsingThreshold()) {
Selim Cinek19c8c702014-08-25 22:09:19 +0200390 mTouchAboveFalsingThreshold = true;
Selim Cinek9db71052015-04-24 18:54:30 -0700391 mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);
Selim Cinek19c8c702014-08-25 22:09:19 +0200392 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800393 if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) &&
394 !isTrackingBlocked()) {
Jorim Jaggicc693242014-06-14 03:04:35 +0000395 setExpandedHeightInternal(newHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100396 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100397 break;
398
399 case MotionEvent.ACTION_UP:
400 case MotionEvent.ACTION_CANCEL:
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700401 addMovement(event);
Selim Cinek547a06b2014-11-27 14:06:04 +0100402 endMotionEvent(event, x, y, false /* forceCancel */);
403 break;
404 }
Selim Cinek9db71052015-04-24 18:54:30 -0700405 return !mGestureWaitForTouchSlop || mTracking;
406 }
407
Evan Laird404a85c2018-02-28 15:31:29 -0500408 private void startOpening(MotionEvent event) {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800409 runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
410 false /* collapseWhenFinished */);
411 notifyBarPanelExpansionChanged();
Matthew Ng5c0592e2018-03-08 14:51:36 -0800412 if (mVibrateOnOpening) {
413 mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
Selim Cinekf6559f82017-08-16 14:49:10 -0700414 }
Evan Laird404a85c2018-02-28 15:31:29 -0500415
416 //TODO: keyguard opens QS a different way; log that too?
417
418 // Log the position of the swipe that opened the panel
419 float width = mStatusBar.getDisplayWidth();
420 float height = mStatusBar.getDisplayHeight();
421 int rot = mStatusBar.getRotation();
422
423 mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
424 (int) (event.getX() / width * 100),
425 (int) (event.getY() / height * 100),
426 rot);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800427 }
428
429 protected abstract float getOpeningHeight();
430
Selim Cinek9db71052015-04-24 18:54:30 -0700431 /**
432 * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
433 * horizontal direction
434 */
435 private boolean isDirectionUpwards(float x, float y) {
436 float xDiff = x - mInitialTouchX;
437 float yDiff = y - mInitialTouchY;
438 if (yDiff >= 0) {
439 return false;
440 }
441 return Math.abs(yDiff) >= Math.abs(xDiff);
Selim Cinek547a06b2014-11-27 14:06:04 +0100442 }
443
Chris Wren621933f2017-06-14 15:59:03 -0400444 protected void startExpandingFromPeek() {
445 mStatusBar.handlePeekToExpandTransistion();
446 }
447
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700448 protected void startExpandMotion(float newX, float newY, boolean startTracking,
449 float expandedHeight) {
450 mInitialOffsetOnTouch = expandedHeight;
451 mInitialTouchY = newY;
452 mInitialTouchX = newX;
453 if (startTracking) {
454 mTouchSlopExceeded = true;
Selim Cinekdef35a82016-05-03 15:52:51 -0700455 setExpandedHeight(mInitialOffsetOnTouch);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700456 onTrackingStarted();
457 }
458 }
459
Selim Cinek547a06b2014-11-27 14:06:04 +0100460 private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
461 mTrackingPointer = -1;
462 if ((mTracking && mTouchSlopExceeded)
463 || Math.abs(x - mInitialTouchX) > mTouchSlop
464 || Math.abs(y - mInitialTouchY) > mTouchSlop
465 || event.getActionMasked() == MotionEvent.ACTION_CANCEL
466 || forceCancel) {
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700467 mVelocityTracker.computeCurrentVelocity(1000);
468 float vel = mVelocityTracker.getYVelocity();
469 float vectorVel = (float) Math.hypot(
470 mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
471
Selim Cinek9db71052015-04-24 18:54:30 -0700472 boolean expand = flingExpands(vel, vectorVel, x, y)
Selim Cinek547a06b2014-11-27 14:06:04 +0100473 || event.getActionMasked() == MotionEvent.ACTION_CANCEL
474 || forceCancel;
Selim Cinek547a06b2014-11-27 14:06:04 +0100475 DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
476 mStatusBar.isFalsingThresholdNeeded(),
Jorim Jaggi50ff3af2015-08-12 18:35:42 -0700477 mStatusBar.isWakeUpComingFromTouch());
Christoph Studerb0183992014-12-22 21:02:26 +0100478 // Log collapse gesture if on lock screen.
Jason Monk1fd3fc32018-08-14 17:20:09 -0400479 if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
Christoph Studerb0183992014-12-22 21:02:26 +0100480 float displayDensity = mStatusBar.getDisplayDensity();
481 int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
482 int velocityDp = (int) Math.abs(vel / displayDensity);
Chris Wren27a52fa2017-02-01 14:21:43 -0500483 mLockscreenGestureLogger.write(
484 MetricsEvent.ACTION_LS_UNLOCK,
Christoph Studerb0183992014-12-22 21:02:26 +0100485 heightDp, velocityDp);
486 }
Selim Cinek9db71052015-04-24 18:54:30 -0700487 fling(vel, expand, isFalseTouch(x, y));
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700488 onTrackingStopped(expand);
Selim Cinek547a06b2014-11-27 14:06:04 +0100489 mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
490 if (mUpdateFlingOnLayout) {
491 mUpdateFlingVelocity = vel;
492 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -0800493 } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
Jason Monk1fd3fc32018-08-14 17:20:09 -0400494 && !mStatusBar.isBouncerShowing() && !mKeyguardMonitor.isKeyguardFadingAway()) {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800495 long timePassed = SystemClock.uptimeMillis() - mDownTime;
496 if (timePassed < ViewConfiguration.getLongPressTimeout()) {
497 // Lets show the user that he can actually expand the panel
498 runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
499 } else {
500 // We need to collapse the panel since we peeked to the small height.
501 postOnAnimation(mPostCollapseRunnable);
502 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -0800503 } else if (!mStatusBar.isBouncerShowing()) {
Selim Cinek547a06b2014-11-27 14:06:04 +0100504 boolean expands = onEmptySpaceClick(mInitialTouchX);
505 onTrackingStopped(expands);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100506 }
Selim Cinek547a06b2014-11-27 14:06:04 +0100507
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700508 mVelocityTracker.clear();
Selim Cinek547a06b2014-11-27 14:06:04 +0100509 mPeekTouching = false;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100510 }
511
Selim Cinekd5ab6452016-12-08 16:34:00 -0800512 protected float getCurrentExpandVelocity() {
Selim Cinekd5ab6452016-12-08 16:34:00 -0800513 mVelocityTracker.computeCurrentVelocity(1000);
514 return mVelocityTracker.getYVelocity();
515 }
516
Selim Cinek29ed3c92014-09-23 20:44:35 +0200517 private int getFalsingThreshold() {
Jorim Jaggi50ff3af2015-08-12 18:35:42 -0700518 float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinek29ed3c92014-09-23 20:44:35 +0200519 return (int) (mUnlockFalsingThreshold * factor);
520 }
521
Selim Cinek4c6969a2014-05-26 19:22:17 +0200522 protected abstract boolean hasConflictingGestures();
523
Selim Cinek9db71052015-04-24 18:54:30 -0700524 protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
525
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200526 protected void onTrackingStopped(boolean expand) {
Selim Cinek4c6969a2014-05-26 19:22:17 +0200527 mTracking = false;
Xiaohui Chen9f967112016-01-07 14:14:06 -0800528 mBar.onTrackingStopped(expand);
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700529 notifyBarPanelExpansionChanged();
Selim Cinek1685e632014-04-08 02:27:49 +0200530 }
531
532 protected void onTrackingStarted() {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200533 endClosing();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200534 mTracking = true;
Xiaohui Chen9f967112016-01-07 14:14:06 -0800535 mBar.onTrackingStarted();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200536 notifyExpandingStarted();
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700537 notifyBarPanelExpansionChanged();
Selim Cinek1685e632014-04-08 02:27:49 +0200538 }
539
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100540 @Override
541 public boolean onInterceptTouchEvent(MotionEvent event) {
Selim Cinekbe2c4432017-05-30 12:11:09 -0700542 if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled
Selim Cinek547a06b2014-11-27 14:06:04 +0100543 || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200544 return false;
545 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100546
547 /*
Chris Wren5d53df42015-06-26 11:26:03 -0400548 * If the user drags anywhere inside the panel we intercept it if the movement is
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100549 * upwards. This allows closing the shade from anywhere inside the panel.
550 *
551 * We only do this if the current content is scrolled to the bottom,
552 * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture
553 * possible.
554 */
555 int pointerIndex = event.findPointerIndex(mTrackingPointer);
556 if (pointerIndex < 0) {
557 pointerIndex = 0;
558 mTrackingPointer = event.getPointerId(pointerIndex);
559 }
Jorim Jaggia6310292014-04-16 14:11:52 +0200560 final float x = event.getX(pointerIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100561 final float y = event.getY(pointerIndex);
562 boolean scrolledToBottom = isScrolledToBottom();
563
564 switch (event.getActionMasked()) {
565 case MotionEvent.ACTION_DOWN:
Jorim Jaggib690f0d2014-07-03 23:25:44 +0200566 mStatusBar.userActivity();
Selim Cinek173f2d02015-04-29 15:36:56 -0700567 mAnimatingOnDown = mHeightAnimator != null;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800568 mMinExpandHeight = 0.0f;
569 mDownTime = SystemClock.uptimeMillis();
570 if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
571 || mPeekAnimator != null) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200572 cancelHeightAnimator();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200573 cancelPeek();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +0200574 mTouchSlopExceeded = true;
Selim Cinek172e9142014-05-07 19:38:00 +0200575 return true;
576 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100577 mInitialTouchY = y;
Jorim Jaggia6310292014-04-16 14:11:52 +0200578 mInitialTouchX = x;
Selim Cinekab1dc952014-10-30 20:20:29 +0100579 mTouchStartedInEmptyArea = !isInContentBounds(x, y);
Jorim Jaggi90129582014-06-02 14:44:49 +0200580 mTouchSlopExceeded = false;
Jorim Jaggi3857ac42014-06-27 18:01:12 +0200581 mJustPeeked = false;
Selim Cinek547a06b2014-11-27 14:06:04 +0100582 mMotionAborted = false;
Selim Cinek684a4422015-04-15 16:18:39 -0700583 mPanelClosedOnDown = isFullyCollapsed();
Selim Cinek79d79c42015-05-21 16:14:45 -0700584 mCollapsedAndHeadsUpOnDown = false;
Selim Cinek31094df2014-08-14 19:28:15 +0200585 mHasLayoutedSinceDown = false;
586 mUpdateFlingOnLayout = false;
Selim Cinek19c8c702014-08-25 22:09:19 +0200587 mTouchAboveFalsingThreshold = false;
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700588 addMovement(event);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100589 break;
590 case MotionEvent.ACTION_POINTER_UP:
591 final int upPointer = event.getPointerId(event.getActionIndex());
592 if (mTrackingPointer == upPointer) {
593 // gesture is ongoing, find a new pointer to track
594 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
595 mTrackingPointer = event.getPointerId(newIndex);
Jorim Jaggia6310292014-04-16 14:11:52 +0200596 mInitialTouchX = event.getX(newIndex);
597 mInitialTouchY = event.getY(newIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100598 }
599 break;
Selim Cinek547a06b2014-11-27 14:06:04 +0100600 case MotionEvent.ACTION_POINTER_DOWN:
Jason Monk1fd3fc32018-08-14 17:20:09 -0400601 if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
Selim Cinek547a06b2014-11-27 14:06:04 +0100602 mMotionAborted = true;
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700603 mVelocityTracker.clear();
Selim Cinek547a06b2014-11-27 14:06:04 +0100604 }
605 break;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100606 case MotionEvent.ACTION_MOVE:
607 final float h = y - mInitialTouchY;
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700608 addMovement(event);
Selim Cinek173f2d02015-04-29 15:36:56 -0700609 if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
610 float hAbs = Math.abs(h);
611 if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
612 && hAbs > Math.abs(x - mInitialTouchX)) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200613 cancelHeightAnimator();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700614 startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100615 return true;
616 }
617 }
618 break;
Selim Cinek31094df2014-08-14 19:28:15 +0200619 case MotionEvent.ACTION_CANCEL:
620 case MotionEvent.ACTION_UP:
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700621 mVelocityTracker.clear();
Selim Cinek31094df2014-08-14 19:28:15 +0200622 break;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100623 }
624 return false;
625 }
626
Selim Cinekab1dc952014-10-30 20:20:29 +0100627 /**
628 * @return Whether a pair of coordinates are inside the visible view content bounds.
629 */
630 protected abstract boolean isInContentBounds(float x, float y);
631
Selim Cinek831941f2015-06-17 15:09:30 -0700632 protected void cancelHeightAnimator() {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200633 if (mHeightAnimator != null) {
Selim Cinekd18b86f2017-06-21 09:44:52 -0700634 if (mHeightAnimator.isRunning()) {
635 mPanelUpdateWhenAnimatorEnds = false;
636 }
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200637 mHeightAnimator.cancel();
638 }
639 endClosing();
640 }
641
642 private void endClosing() {
643 if (mClosing) {
644 mClosing = false;
645 onClosingFinished();
646 }
647 }
648
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100649 protected boolean isScrolledToBottom() {
Selim Cinek172e9142014-05-07 19:38:00 +0200650 return true;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100651 }
652
653 protected float getContentHeight() {
654 return mExpandedHeight;
Daniel Sandlerbf526d12012-09-04 22:56:44 -0400655 }
656
Daniel Sandler08d05e32012-08-08 16:39:54 -0400657 @Override
658 protected void onFinishInflate() {
659 super.onFinishInflate();
660 loadDimens();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400661 }
662
Jorim Jaggi069cd032014-05-15 03:09:01 +0200663 @Override
664 protected void onConfigurationChanged(Configuration newConfig) {
665 super.onConfigurationChanged(newConfig);
666 loadDimens();
Jorim Jaggi069cd032014-05-15 03:09:01 +0200667 }
668
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200669 /**
Jorim Jaggib7240132014-06-30 01:39:07 +0200670 * @param vel the current vertical velocity of the motion
671 * @param vectorVel the length of the vectorial velocity
Jorim Jaggie29b2db2014-05-30 23:17:03 +0200672 * @return whether a fling should expands the panel; contracts otherwise
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200673 */
Selim Cinek9db71052015-04-24 18:54:30 -0700674 protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
Dave Mankoffc88d6222018-10-25 15:31:20 -0400675 if (mFalsingManager.isUnlockingDisabled()) {
676 return true;
677 }
678
Selim Cinek9db71052015-04-24 18:54:30 -0700679 if (isFalseTouch(x, y)) {
Selim Cinek19c8c702014-08-25 22:09:19 +0200680 return true;
681 }
Jorim Jaggib7240132014-06-30 01:39:07 +0200682 if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
Jorim Jaggie29b2db2014-05-30 23:17:03 +0200683 return getExpandedFraction() > 0.5f;
Selim Cinek1685e632014-04-08 02:27:49 +0200684 } else {
Jorim Jaggie29b2db2014-05-30 23:17:03 +0200685 return vel > 0;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400686 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200687 }
688
Selim Cinek9db71052015-04-24 18:54:30 -0700689 /**
690 * @param x the final x-coordinate when the finger was lifted
691 * @param y the final y-coordinate when the finger was lifted
692 * @return whether this motion should be regarded as a false touch
693 */
694 private boolean isFalseTouch(float x, float y) {
695 if (!mStatusBar.isFalsingThresholdNeeded()) {
696 return false;
697 }
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700698 if (mFalsingManager.isClassiferEnabled()) {
699 return mFalsingManager.isFalseTouch();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700700 }
Selim Cinek9db71052015-04-24 18:54:30 -0700701 if (!mTouchAboveFalsingThreshold) {
702 return true;
703 }
704 if (mUpwardsWhenTresholdReached) {
705 return false;
706 }
707 return !isDirectionUpwards(x, y);
Selim Cinek5386fb32014-09-03 16:37:36 +0200708 }
709
Jorim Jaggi1d480692014-05-20 19:41:58 +0200710 protected void fling(float vel, boolean expand) {
Selim Cinek9db71052015-04-24 18:54:30 -0700711 fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700712 }
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200713
Selim Cinek9db71052015-04-24 18:54:30 -0700714 protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
715 fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
716 }
717
718 protected void fling(float vel, boolean expand, float collapseSpeedUpFactor,
719 boolean expandBecauseOfFalsing) {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700720 cancelPeek();
Selim Cinek48ff9b42016-11-09 19:31:51 -0800721 float target = expand ? getMaxPanelHeight() : 0;
Jorim Jaggi3e02adb2015-05-13 17:50:26 -0700722 if (!expand) {
723 mClosing = true;
724 }
Selim Cinek9db71052015-04-24 18:54:30 -0700725 flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700726 }
727
728 protected void flingToHeight(float vel, boolean expand, float target,
Selim Cinek9db71052015-04-24 18:54:30 -0700729 float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200730 // Hack to make the expand transition look nice when clear all button is visible - we make
731 // the animation only to the last notification, and then jump to the maximum panel height so
732 // clear all just fades in and the decelerating motion is towards the last notification.
733 final boolean clearAllExpandHack = expand && fullyExpandedClearAllVisible()
734 && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
735 && !isClearAllVisible();
736 if (clearAllExpandHack) {
737 target = getMaxPanelHeight() - getClearAllHeight();
738 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200739 if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200740 notifyExpandingFinished();
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200741 return;
742 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200743 mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
Jorim Jaggi90129582014-06-02 14:44:49 +0200744 ValueAnimator animator = createHeightAnimator(target);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200745 if (expand) {
Selim Cinek61190fb2016-12-07 12:16:45 -0800746 if (expandBecauseOfFalsing && vel < 0) {
Selim Cinek5386fb32014-09-03 16:37:36 +0200747 vel = 0;
748 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200749 mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
Jorim Jaggi6626f542016-08-22 13:08:44 -0700750 if (vel == 0) {
Selim Cinek5386fb32014-09-03 16:37:36 +0200751 animator.setDuration(350);
752 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200753 } else {
Selim Cinek2411f762016-12-28 17:05:08 +0100754 if (shouldUseDismissingAnimation()) {
Selim Cinekdf5501b2017-04-14 20:01:27 -0700755 if (vel == 0) {
756 animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
757 long duration = (long) (200 + mExpandedHeight / getHeight() * 100);
758 animator.setDuration(duration);
759 } else {
760 mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
761 getHeight());
762 }
Selim Cinek2411f762016-12-28 17:05:08 +0100763 } else {
764 mFlingAnimationUtilsClosing
765 .apply(animator, mExpandedHeight, target, vel, getHeight());
766 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200767
768 // Make it shorter if we run a canned animation
769 if (vel == 0) {
Selim Cinek593ee202016-12-28 15:01:16 +0100770 animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
Jorim Jaggi1d480692014-05-20 19:41:58 +0200771 }
Selim Cinek2627d722018-01-19 12:16:49 -0800772 if (mFixedDuration != NO_FIXED_DURATION) {
773 animator.setDuration(mFixedDuration);
774 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200775 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200776 animator.addListener(new AnimatorListenerAdapter() {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200777 private boolean mCancelled;
778
779 @Override
780 public void onAnimationCancel(Animator animation) {
781 mCancelled = true;
782 }
783
Jorim Jaggi1d480692014-05-20 19:41:58 +0200784 @Override
785 public void onAnimationEnd(Animator animation) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200786 if (clearAllExpandHack && !mCancelled) {
Jorim Jaggi2ae259d2014-08-04 23:35:47 +0200787 setExpandedHeightInternal(getMaxPanelHeight());
788 }
Selim Cinekbe2c4432017-05-30 12:11:09 -0700789 setAnimator(null);
Jorim Jaggi2ae259d2014-08-04 23:35:47 +0200790 if (!mCancelled) {
791 notifyExpandingFinished();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200792 }
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700793 notifyBarPanelExpansionChanged();
Jorim Jaggi1d480692014-05-20 19:41:58 +0200794 }
795 });
Selim Cinekbe2c4432017-05-30 12:11:09 -0700796 setAnimator(animator);
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200797 animator.start();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400798 }
799
Selim Cinek2411f762016-12-28 17:05:08 +0100800 protected abstract boolean shouldUseDismissingAnimation();
801
Daniel Sandler08d05e32012-08-08 16:39:54 -0400802 @Override
803 protected void onAttachedToWindow() {
804 super.onAttachedToWindow();
Daniel Sandler978f8532012-08-15 15:48:16 -0400805 mViewName = getResources().getResourceName(getId());
806 }
807
808 public String getName() {
809 return mViewName;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400810 }
811
Daniel Sandler08d05e32012-08-08 16:39:54 -0400812 public void setExpandedHeight(float height) {
John Spurlock97642182013-07-29 17:58:39 -0400813 if (DEBUG) logf("setExpandedHeight(%.1f)", height);
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200814 setExpandedHeightInternal(height + getOverExpansionPixels());
Daniel Sandler08d05e32012-08-08 16:39:54 -0400815 }
816
Daniel Sandler50508132012-08-16 14:10:53 -0400817 @Override
Lucas Dupin60661a62018-04-12 10:50:13 -0700818 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Daniel Sandler50508132012-08-16 14:10:53 -0400819 super.onLayout(changed, left, top, right, bottom);
Selim Cinek5fbc6322016-01-15 17:17:58 -0800820 mStatusBar.onPanelLaidOut();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100821 requestPanelHeightUpdate();
Selim Cinek31094df2014-08-14 19:28:15 +0200822 mHasLayoutedSinceDown = true;
823 if (mUpdateFlingOnLayout) {
824 abortAnimations();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700825 fling(mUpdateFlingVelocity, true /* expands */);
Selim Cinek31094df2014-08-14 19:28:15 +0200826 mUpdateFlingOnLayout = false;
827 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100828 }
829
830 protected void requestPanelHeightUpdate() {
831 float currentMaxPanelHeight = getMaxPanelHeight();
832
Selim Cinekbe2c4432017-05-30 12:11:09 -0700833 if (isFullyCollapsed()) {
834 return;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100835 }
Selim Cinekbe2c4432017-05-30 12:11:09 -0700836
837 if (currentMaxPanelHeight == mExpandedHeight) {
838 return;
839 }
840
841 if (mPeekAnimator != null || mPeekTouching) {
842 return;
843 }
844
845 if (mTracking && !isTrackingBlocked()) {
846 return;
847 }
848
849 if (mHeightAnimator != null) {
850 mPanelUpdateWhenAnimatorEnds = true;
851 return;
852 }
853
854 setExpandedHeight(currentMaxPanelHeight);
Daniel Sandler50508132012-08-16 14:10:53 -0400855 }
856
Daniel Sandler08d05e32012-08-08 16:39:54 -0400857 public void setExpandedHeightInternal(float h) {
Jorim Jaggie05256e2016-09-08 14:58:20 -0700858 if (mExpandLatencyTracking && h != 0f) {
859 DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(mContext).onActionEnd(
860 LatencyTracker.ACTION_EXPAND_PANEL));
861 mExpandLatencyTracking = false;
862 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200863 float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
864 if (mHeightAnimator == null) {
865 float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
866 if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
867 setOverExpansion(overExpansionPixels, true /* isPixels */);
868 }
869 mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
870 } else {
871 mExpandedHeight = h;
872 if (mOverExpandedBeforeFling) {
873 setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
874 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100875 }
Daniel Sandler198a0302012-08-17 16:04:31 -0400876
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200877 // If we are closing the panel and we are almost there due to a slow decelerating
878 // interpolator, abort the animation.
879 if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
880 mExpandedHeight = 0f;
881 if (mHeightAnimator != null) {
882 mHeightAnimator.end();
883 }
884 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800885 mExpandedFraction = Math.min(1f,
886 fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100887 onHeightUpdated(mExpandedHeight);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200888 notifyBarPanelExpansionChanged();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400889 }
890
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200891 /**
892 * @return true if the panel tracking should be temporarily blocked; this is used when a
893 * conflicting gesture (opening QS) is happening
894 */
895 protected abstract boolean isTrackingBlocked();
896
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200897 protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
Selim Cinek24120a52014-05-26 10:05:42 +0200898
Jorim Jaggi90129582014-06-02 14:44:49 +0200899 protected abstract void onHeightUpdated(float expandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100900
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200901 protected abstract float getOverExpansionAmount();
902
903 protected abstract float getOverExpansionPixels();
904
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100905 /**
906 * This returns the maximum height of the panel. Children should override this if their
907 * desired height is not the full height.
908 *
909 * @return the default implementation simply returns the maximum height.
910 */
Selim Cinek31094df2014-08-14 19:28:15 +0200911 protected abstract int getMaxPanelHeight();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400912
913 public void setExpandedFraction(float frac) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100914 setExpandedHeight(getMaxPanelHeight() * frac);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400915 }
916
917 public float getExpandedHeight() {
918 return mExpandedHeight;
919 }
920
921 public float getExpandedFraction() {
922 return mExpandedFraction;
923 }
924
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700925 public boolean isFullyExpanded() {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100926 return mExpandedHeight >= getMaxPanelHeight();
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700927 }
928
929 public boolean isFullyCollapsed() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800930 return mExpandedFraction <= 0.0f;
Daniel Sandler67eab792012-10-02 17:08:23 -0400931 }
932
933 public boolean isCollapsing() {
Selim Cinek7e222c3c2018-01-25 12:22:41 -0800934 return mClosing || mLaunchingNotification;
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700935 }
936
John Spurlocka4b70af2013-08-17 14:05:49 -0400937 public boolean isTracking() {
938 return mTracking;
939 }
940
Daniel Sandler08d05e32012-08-08 16:39:54 -0400941 public void setBar(PanelBar panelBar) {
942 mBar = panelBar;
943 }
944
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700945 public void collapse(boolean delayed, float speedUpFactor) {
John Spurlock97642182013-07-29 17:58:39 -0400946 if (DEBUG) logf("collapse: " + this);
Selim Cinek9bb05632016-12-15 14:14:29 -0800947 if (canPanelBeCollapsed()) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200948 cancelHeightAnimator();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200949 notifyExpandingStarted();
Jorim Jaggi3b866be2015-06-30 11:31:10 -0700950
951 // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
952 mClosing = true;
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200953 if (delayed) {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700954 mNextCollapseSpeedUpFactor = speedUpFactor;
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200955 postDelayed(mFlingCollapseRunnable, 120);
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200956 } else {
Selim Cinek9db71052015-04-24 18:54:30 -0700957 fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200958 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400959 }
960 }
961
Selim Cinek9bb05632016-12-15 14:14:29 -0800962 public boolean canPanelBeCollapsed() {
963 return !isFullyCollapsed() && !mTracking && !mClosing;
964 }
965
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200966 private final Runnable mFlingCollapseRunnable = new Runnable() {
967 @Override
968 public void run() {
Selim Cinek9db71052015-04-24 18:54:30 -0700969 fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
970 false /* expandBecauseOfFalsing */);
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200971 }
972 };
973
John Spurlock97642182013-07-29 17:58:39 -0400974 public void cancelPeek() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800975 boolean cancelled = false;
Jorim Jaggib472b3472014-06-30 19:56:24 +0200976 if (mPeekAnimator != null) {
Selim Cinek529c5322016-04-06 20:03:45 -0700977 cancelled = true;
John Spurlock97642182013-07-29 17:58:39 -0400978 mPeekAnimator.cancel();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400979 }
Jorim Jaggib472b3472014-06-30 19:56:24 +0200980
Selim Cinek529c5322016-04-06 20:03:45 -0700981 if (cancelled) {
982 // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
983 // notify mBar that we might have closed ourselves.
984 notifyBarPanelExpansionChanged();
985 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400986 }
Daniel Sandler37a38aa2013-02-13 17:15:57 -0500987
Oren Blasberg8d3fea12015-07-10 14:21:44 -0700988 public void expand(final boolean animate) {
989 if (!isFullyCollapsed() && !isCollapsing()) {
990 return;
991 }
992
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200993 mInstantExpanding = true;
Jorim Jaggi6626f542016-08-22 13:08:44 -0700994 mAnimateAfterExpanding = animate;
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200995 mUpdateFlingOnLayout = false;
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200996 abortAnimations();
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200997 cancelPeek();
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200998 if (mTracking) {
999 onTrackingStopped(true /* expands */); // The panel is expanded after this call.
Jorim Jaggie62d5892014-09-02 16:31:19 +02001000 }
1001 if (mExpanding) {
Jorim Jaggib472b3472014-06-30 19:56:24 +02001002 notifyExpandingFinished();
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001003 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001004 notifyBarPanelExpansionChanged();
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001005
1006 // Wait for window manager to pickup the change, so we know the maximum height of the panel
1007 // then.
1008 getViewTreeObserver().addOnGlobalLayoutListener(
1009 new ViewTreeObserver.OnGlobalLayoutListener() {
1010 @Override
1011 public void onGlobalLayout() {
Jorim Jaggic5804af2016-04-25 18:51:16 -07001012 if (!mInstantExpanding) {
1013 getViewTreeObserver().removeOnGlobalLayoutListener(this);
1014 return;
1015 }
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001016 if (mStatusBar.getStatusBarWindow().getHeight()
1017 != mStatusBar.getStatusBarHeight()) {
1018 getViewTreeObserver().removeOnGlobalLayoutListener(this);
Jorim Jaggi6626f542016-08-22 13:08:44 -07001019 if (mAnimateAfterExpanding) {
Oren Blasberg8d3fea12015-07-10 14:21:44 -07001020 notifyExpandingStarted();
1021 fling(0, true /* expand */);
1022 } else {
1023 setExpandedFraction(1f);
1024 }
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001025 mInstantExpanding = false;
1026 }
1027 }
1028 });
1029
1030 // Make sure a layout really happens.
1031 requestLayout();
1032 }
1033
Selim Cinek6bb4a9b2014-10-09 17:48:05 -07001034 public void instantCollapse() {
1035 abortAnimations();
1036 setExpandedFraction(0f);
1037 if (mExpanding) {
1038 notifyExpandingFinished();
1039 }
Jorim Jaggic5804af2016-04-25 18:51:16 -07001040 if (mInstantExpanding) {
1041 mInstantExpanding = false;
1042 notifyBarPanelExpansionChanged();
1043 }
Selim Cinek6bb4a9b2014-10-09 17:48:05 -07001044 }
1045
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001046 private void abortAnimations() {
1047 cancelPeek();
Selim Cinekdbbcfbe2014-10-24 17:52:35 +02001048 cancelHeightAnimator();
Jorim Jaggi5cbfe542014-09-10 22:23:08 +02001049 removeCallbacks(mPostCollapseRunnable);
1050 removeCallbacks(mFlingCollapseRunnable);
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001051 }
1052
Selim Cinekdbbcfbe2014-10-24 17:52:35 +02001053 protected void onClosingFinished() {
1054 mBar.onClosingFinished();
1055 }
1056
1057
Jorim Jaggi90129582014-06-02 14:44:49 +02001058 protected void startUnlockHintAnimation() {
1059
1060 // We don't need to hint the user if an animation is already running or the user is changing
1061 // the expansion.
1062 if (mHeightAnimator != null || mTracking) {
1063 return;
1064 }
1065 cancelPeek();
Jorim Jaggib472b3472014-06-30 19:56:24 +02001066 notifyExpandingStarted();
Lucas Dupin60661a62018-04-12 10:50:13 -07001067 startUnlockHintAnimationPhase1(() -> {
1068 notifyExpandingFinished();
1069 onUnlockHintFinished();
1070 mHintAnimationRunning = false;
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001071 });
Selim Cinekec29d342017-05-05 18:31:49 -07001072 onUnlockHintStarted();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001073 mHintAnimationRunning = true;
Jorim Jaggi90129582014-06-02 14:44:49 +02001074 }
1075
Selim Cinekec29d342017-05-05 18:31:49 -07001076 protected void onUnlockHintFinished() {
1077 mStatusBar.onHintFinished();
1078 }
1079
1080 protected void onUnlockHintStarted() {
1081 mStatusBar.onUnlockHintStarted();
1082 }
1083
Lucas Dupinbc9aac12018-03-04 20:18:15 -08001084 public boolean isUnlockHintRunning() {
1085 return mHintAnimationRunning;
1086 }
1087
Jorim Jaggi90129582014-06-02 14:44:49 +02001088 /**
1089 * Phase 1: Move everything upwards.
1090 */
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001091 private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
Jorim Jaggi90129582014-06-02 14:44:49 +02001092 float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
1093 ValueAnimator animator = createHeightAnimator(target);
1094 animator.setDuration(250);
Selim Cinekc18010f2016-01-20 13:41:30 -08001095 animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
Jorim Jaggi90129582014-06-02 14:44:49 +02001096 animator.addListener(new AnimatorListenerAdapter() {
1097 private boolean mCancelled;
1098
1099 @Override
1100 public void onAnimationCancel(Animator animation) {
1101 mCancelled = true;
1102 }
1103
1104 @Override
1105 public void onAnimationEnd(Animator animation) {
1106 if (mCancelled) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07001107 setAnimator(null);
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001108 onAnimationFinished.run();
Jorim Jaggi90129582014-06-02 14:44:49 +02001109 } else {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001110 startUnlockHintAnimationPhase2(onAnimationFinished);
Jorim Jaggi90129582014-06-02 14:44:49 +02001111 }
1112 }
1113 });
1114 animator.start();
Selim Cinekbe2c4432017-05-30 12:11:09 -07001115 setAnimator(animator);
Adrian Roos8d4e99f2017-05-25 18:03:58 -07001116
1117 View[] viewsToAnimate = {
1118 mKeyguardBottomArea.getIndicationArea(),
1119 mStatusBar.getAmbientIndicationContainer()};
1120 for (View v : viewsToAnimate) {
1121 if (v == null) {
1122 continue;
1123 }
1124 v.animate()
1125 .translationY(-mHintDistance)
1126 .setDuration(250)
1127 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
1128 .withEndAction(() -> v.animate()
1129 .translationY(0)
1130 .setDuration(450)
1131 .setInterpolator(mBounceInterpolator)
1132 .start())
1133 .start();
1134 }
Jorim Jaggi90129582014-06-02 14:44:49 +02001135 }
1136
Selim Cinekbe2c4432017-05-30 12:11:09 -07001137 private void setAnimator(ValueAnimator animator) {
1138 mHeightAnimator = animator;
1139 if (animator == null && mPanelUpdateWhenAnimatorEnds) {
1140 mPanelUpdateWhenAnimatorEnds = false;
1141 requestPanelHeightUpdate();
1142 }
1143 }
1144
Jorim Jaggi90129582014-06-02 14:44:49 +02001145 /**
1146 * Phase 2: Bounce down.
1147 */
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001148 private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
Jorim Jaggi90129582014-06-02 14:44:49 +02001149 ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
1150 animator.setDuration(450);
1151 animator.setInterpolator(mBounceInterpolator);
1152 animator.addListener(new AnimatorListenerAdapter() {
1153 @Override
1154 public void onAnimationEnd(Animator animation) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07001155 setAnimator(null);
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001156 onAnimationFinished.run();
Jorim Jaggieb8f11a2015-05-20 18:42:16 -07001157 notifyBarPanelExpansionChanged();
Jorim Jaggi90129582014-06-02 14:44:49 +02001158 }
1159 });
1160 animator.start();
Selim Cinekbe2c4432017-05-30 12:11:09 -07001161 setAnimator(animator);
Jorim Jaggi90129582014-06-02 14:44:49 +02001162 }
1163
1164 private ValueAnimator createHeightAnimator(float targetHeight) {
1165 ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
Lucas Dupin2e838ac2019-04-17 16:50:58 -07001166 animator.addUpdateListener(
1167 animation -> setExpandedHeightInternal((float) animation.getAnimatedValue()));
Jorim Jaggi90129582014-06-02 14:44:49 +02001168 return animator;
1169 }
1170
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001171 protected void notifyBarPanelExpansionChanged() {
Evan Laird0de26b02019-03-01 15:12:50 -05001172 if (mBar != null) {
Aran Inkb6aadb42019-05-16 14:22:12 -04001173 mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f
Evan Laird0de26b02019-03-01 15:12:50 -05001174 || mPeekAnimator != null || mInstantExpanding
1175 || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null);
1176 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -08001177 if (mExpansionListener != null) {
Aran Inkb6aadb42019-05-16 14:22:12 -04001178 mExpansionListener.onPanelExpansionChanged(mExpandedFraction, mTracking);
Lucas Dupinbc9aac12018-03-04 20:18:15 -08001179 }
1180 }
1181
Lucas Dupin2e838ac2019-04-17 16:50:58 -07001182 public void setExpansionListener(PanelExpansionListener panelExpansionListener) {
1183 mExpansionListener = panelExpansionListener;
Jorim Jaggib472b3472014-06-30 19:56:24 +02001184 }
1185
Selim Cinek0fccc722015-07-29 17:04:36 -07001186 protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
1187
Jorim Jaggi90129582014-06-02 14:44:49 +02001188 /**
1189 * Gets called when the user performs a click anywhere in the empty area of the panel.
1190 *
1191 * @return whether the panel will be expanded after the action performed by this method
1192 */
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001193 protected boolean onEmptySpaceClick(float x) {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001194 if (mHintAnimationRunning) {
1195 return true;
1196 }
Selim Cinek6746c282015-04-21 19:58:31 -07001197 return onMiddleClicked();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001198 }
1199
Jorim Jaggi1cf6e102015-01-20 16:45:05 +01001200 protected final Runnable mPostCollapseRunnable = new Runnable() {
Jorim Jaggi488b7922014-08-05 21:12:02 +02001201 @Override
1202 public void run() {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -07001203 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
Jorim Jaggi488b7922014-08-05 21:12:02 +02001204 }
1205 };
Jorim Jaggi488b7922014-08-05 21:12:02 +02001206
Jorim Jaggi1cf6e102015-01-20 16:45:05 +01001207 protected abstract boolean onMiddleClicked();
Jorim Jaggi90129582014-06-02 14:44:49 +02001208
Jorim Jaggid7912d22014-09-30 17:38:19 +02001209 protected abstract boolean isDozing();
1210
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001211 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Selim Cineka3e5bcb2014-04-07 20:07:22 +02001212 pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
Jorim Jaggi50d87a72014-09-12 21:43:35 +02001213 + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s touchDisabled=%s"
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001214 + "]",
1215 this.getClass().getSimpleName(),
1216 getExpandedHeight(),
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001217 getMaxPanelHeight(),
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001218 mClosing?"T":"f",
1219 mTracking?"T":"f",
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001220 mJustPeeked?"T":"f",
1221 mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""),
Jorim Jaggi50d87a72014-09-12 21:43:35 +02001222 mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":""),
1223 mTouchDisabled?"T":"f"
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001224 ));
1225 }
Selim Cinek3c4635c2014-05-29 02:12:47 +02001226
Lucas Dupinee63c372018-08-03 11:58:24 -07001227 public abstract void resetViews(boolean animate);
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001228
1229 protected abstract float getPeekHeight();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02001230 /**
1231 * @return whether "Clear all" button will be visible when the panel is fully expanded
1232 */
1233 protected abstract boolean fullyExpandedClearAllVisible();
1234
1235 protected abstract boolean isClearAllVisible();
1236
1237 /**
1238 * @return the height of the clear all button, in pixels
1239 */
1240 protected abstract int getClearAllHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001241
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001242 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001243 mHeadsUpManager = headsUpManager;
1244 }
Selim Cinek2627d722018-01-19 12:16:49 -08001245
1246 public void setLaunchingNotification(boolean launchingNotification) {
1247 mLaunchingNotification = launchingNotification;
1248 }
1249
1250 public void collapseWithDuration(int animationDuration) {
1251 mFixedDuration = animationDuration;
1252 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
1253 mFixedDuration = NO_FIXED_DURATION;
1254 }
Daniel Sandler08d05e32012-08-08 16:39:54 -04001255}