blob: 31600e391f34f11322bfc131f939c101dd1327e4 [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;
John Spurlock813552c2014-09-19 08:30:21 -040045import com.android.systemui.doze.DozeLog;
Dave Mankoff468d4f62019-05-08 14:56:29 -040046import com.android.systemui.plugins.FalsingManager;
Beverly8fdb5332019-02-04 14:29:49 -050047import com.android.systemui.plugins.statusbar.StatusBarStateController;
Jorim Jaggi1d480692014-05-20 19:41:58 +020048import com.android.systemui.statusbar.FlingAnimationUtils;
Jorim Jaggi90129582014-06-02 14:44:49 +020049import com.android.systemui.statusbar.StatusBarState;
Beverly8fdb5332019-02-04 14:29:49 -050050import com.android.systemui.statusbar.SysuiStatusBarStateController;
Matthew Ng5c0592e2018-03-08 14:51:36 -080051import com.android.systemui.statusbar.VibratorHelper;
Jason Monk1fd3fc32018-08-14 17:20:09 -040052import com.android.systemui.statusbar.policy.KeyguardMonitor;
Daniel Sandler08d05e32012-08-08 16:39:54 -040053
John Spurlockde84f0e2013-06-12 12:41:00 -040054import java.io.FileDescriptor;
55import java.io.PrintWriter;
Selim Cineka678b0e2019-07-23 13:12:55 -070056import java.util.ArrayList;
John Spurlockde84f0e2013-06-12 12:41:00 -040057
Selim Cinek4c6969a2014-05-26 19:22:17 +020058public abstract class PanelView extends FrameLayout {
Daniel Sandler198a0302012-08-17 16:04:31 -040059 public static final boolean DEBUG = PanelBar.DEBUG;
Daniel Sandler08d05e32012-08-08 16:39:54 -040060 public static final String TAG = PanelView.class.getSimpleName();
Selim Cinek48ff9b42016-11-09 19:31:51 -080061 private static final int INITIAL_OPENING_PEEK_DURATION = 200;
62 private static final int PEEK_ANIMATION_DURATION = 360;
Selim Cinek2627d722018-01-19 12:16:49 -080063 private static final int NO_FIXED_DURATION = -1;
Selim Cinek08bd435a2019-07-26 11:37:14 -070064 protected long mDownTime;
65 protected boolean mTouchSlopExceededBeforeDown;
Selim Cinek48ff9b42016-11-09 19:31:51 -080066 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;
Selim Cineka678b0e2019-07-23 13:12:55 -070072 protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>();
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;
Dave Mankoff781ef7e2019-06-28 16:33:25 -0400113 private final 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 Mankoff781ef7e2019-06-28 16:33:25 -0400216 mFalsingManager = Dependency.get(FalsingManager.class); // TODO: inject into a controller.
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) {
Sunny Goyalb59e15e2019-06-13 17:02:32 -0700304 mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
Selim Cinek684a4422015-04-15 16:18:39 -0700305 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 Cinek08bd435a2019-07-26 11:37:14 -0700326 || mPeekAnimator != null || mTouchSlopExceededBeforeDown;
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();
Selim Cinek08bd435a2019-07-26 11:37:14 -0700412 maybeVibrateOnOpening();
Evan Laird404a85c2018-02-28 15:31:29 -0500413
414 //TODO: keyguard opens QS a different way; log that too?
415
416 // Log the position of the swipe that opened the panel
417 float width = mStatusBar.getDisplayWidth();
418 float height = mStatusBar.getDisplayHeight();
419 int rot = mStatusBar.getRotation();
420
421 mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
422 (int) (event.getX() / width * 100),
423 (int) (event.getY() / height * 100),
424 rot);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800425 }
426
Selim Cinek08bd435a2019-07-26 11:37:14 -0700427 protected void maybeVibrateOnOpening() {
428 if (mVibrateOnOpening) {
429 mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
430 }
431 }
432
Selim Cinek48ff9b42016-11-09 19:31:51 -0800433 protected abstract float getOpeningHeight();
434
Selim Cinek9db71052015-04-24 18:54:30 -0700435 /**
436 * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
437 * horizontal direction
438 */
439 private boolean isDirectionUpwards(float x, float y) {
440 float xDiff = x - mInitialTouchX;
441 float yDiff = y - mInitialTouchY;
442 if (yDiff >= 0) {
443 return false;
444 }
445 return Math.abs(yDiff) >= Math.abs(xDiff);
Selim Cinek547a06b2014-11-27 14:06:04 +0100446 }
447
Chris Wren621933f2017-06-14 15:59:03 -0400448 protected void startExpandingFromPeek() {
449 mStatusBar.handlePeekToExpandTransistion();
450 }
451
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700452 protected void startExpandMotion(float newX, float newY, boolean startTracking,
453 float expandedHeight) {
454 mInitialOffsetOnTouch = expandedHeight;
455 mInitialTouchY = newY;
456 mInitialTouchX = newX;
457 if (startTracking) {
458 mTouchSlopExceeded = true;
Selim Cinekdef35a82016-05-03 15:52:51 -0700459 setExpandedHeight(mInitialOffsetOnTouch);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700460 onTrackingStarted();
461 }
462 }
463
Selim Cinek547a06b2014-11-27 14:06:04 +0100464 private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
465 mTrackingPointer = -1;
466 if ((mTracking && mTouchSlopExceeded)
467 || Math.abs(x - mInitialTouchX) > mTouchSlop
468 || Math.abs(y - mInitialTouchY) > mTouchSlop
469 || event.getActionMasked() == MotionEvent.ACTION_CANCEL
470 || forceCancel) {
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700471 mVelocityTracker.computeCurrentVelocity(1000);
472 float vel = mVelocityTracker.getYVelocity();
473 float vectorVel = (float) Math.hypot(
474 mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
475
Selim Cinek9db71052015-04-24 18:54:30 -0700476 boolean expand = flingExpands(vel, vectorVel, x, y)
Selim Cinek547a06b2014-11-27 14:06:04 +0100477 || event.getActionMasked() == MotionEvent.ACTION_CANCEL
478 || forceCancel;
Selim Cinek547a06b2014-11-27 14:06:04 +0100479 DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
480 mStatusBar.isFalsingThresholdNeeded(),
Jorim Jaggi50ff3af2015-08-12 18:35:42 -0700481 mStatusBar.isWakeUpComingFromTouch());
Christoph Studerb0183992014-12-22 21:02:26 +0100482 // Log collapse gesture if on lock screen.
Jason Monk1fd3fc32018-08-14 17:20:09 -0400483 if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
Christoph Studerb0183992014-12-22 21:02:26 +0100484 float displayDensity = mStatusBar.getDisplayDensity();
485 int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
486 int velocityDp = (int) Math.abs(vel / displayDensity);
Chris Wren27a52fa2017-02-01 14:21:43 -0500487 mLockscreenGestureLogger.write(
488 MetricsEvent.ACTION_LS_UNLOCK,
Christoph Studerb0183992014-12-22 21:02:26 +0100489 heightDp, velocityDp);
490 }
Selim Cinek9db71052015-04-24 18:54:30 -0700491 fling(vel, expand, isFalseTouch(x, y));
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700492 onTrackingStopped(expand);
Selim Cinek547a06b2014-11-27 14:06:04 +0100493 mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
494 if (mUpdateFlingOnLayout) {
495 mUpdateFlingVelocity = vel;
496 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -0800497 } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
Jason Monk1fd3fc32018-08-14 17:20:09 -0400498 && !mStatusBar.isBouncerShowing() && !mKeyguardMonitor.isKeyguardFadingAway()) {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800499 long timePassed = SystemClock.uptimeMillis() - mDownTime;
500 if (timePassed < ViewConfiguration.getLongPressTimeout()) {
501 // Lets show the user that he can actually expand the panel
502 runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
503 } else {
504 // We need to collapse the panel since we peeked to the small height.
505 postOnAnimation(mPostCollapseRunnable);
506 }
Lucas Dupinbc9aac12018-03-04 20:18:15 -0800507 } else if (!mStatusBar.isBouncerShowing()) {
Selim Cinek547a06b2014-11-27 14:06:04 +0100508 boolean expands = onEmptySpaceClick(mInitialTouchX);
509 onTrackingStopped(expands);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100510 }
Selim Cinek547a06b2014-11-27 14:06:04 +0100511
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700512 mVelocityTracker.clear();
Selim Cinek547a06b2014-11-27 14:06:04 +0100513 mPeekTouching = false;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100514 }
515
Selim Cinekd5ab6452016-12-08 16:34:00 -0800516 protected float getCurrentExpandVelocity() {
Selim Cinekd5ab6452016-12-08 16:34:00 -0800517 mVelocityTracker.computeCurrentVelocity(1000);
518 return mVelocityTracker.getYVelocity();
519 }
520
Selim Cinek29ed3c92014-09-23 20:44:35 +0200521 private int getFalsingThreshold() {
Jorim Jaggi50ff3af2015-08-12 18:35:42 -0700522 float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinek29ed3c92014-09-23 20:44:35 +0200523 return (int) (mUnlockFalsingThreshold * factor);
524 }
525
Sunny Goyalb59e15e2019-06-13 17:02:32 -0700526 protected abstract boolean shouldGestureWaitForTouchSlop();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200527
Selim Cinek9db71052015-04-24 18:54:30 -0700528 protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
529
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200530 protected void onTrackingStopped(boolean expand) {
Selim Cinek4c6969a2014-05-26 19:22:17 +0200531 mTracking = false;
Xiaohui Chen9f967112016-01-07 14:14:06 -0800532 mBar.onTrackingStopped(expand);
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700533 notifyBarPanelExpansionChanged();
Selim Cinek1685e632014-04-08 02:27:49 +0200534 }
535
536 protected void onTrackingStarted() {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200537 endClosing();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200538 mTracking = true;
Xiaohui Chen9f967112016-01-07 14:14:06 -0800539 mBar.onTrackingStarted();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200540 notifyExpandingStarted();
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700541 notifyBarPanelExpansionChanged();
Selim Cinek1685e632014-04-08 02:27:49 +0200542 }
543
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100544 @Override
545 public boolean onInterceptTouchEvent(MotionEvent event) {
Selim Cinekbe2c4432017-05-30 12:11:09 -0700546 if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled
Selim Cinek547a06b2014-11-27 14:06:04 +0100547 || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
Jorim Jaggi0a27be82014-06-11 03:22:39 +0200548 return false;
549 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100550
551 /*
Chris Wren5d53df42015-06-26 11:26:03 -0400552 * If the user drags anywhere inside the panel we intercept it if the movement is
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100553 * upwards. This allows closing the shade from anywhere inside the panel.
554 *
555 * We only do this if the current content is scrolled to the bottom,
556 * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture
557 * possible.
558 */
559 int pointerIndex = event.findPointerIndex(mTrackingPointer);
560 if (pointerIndex < 0) {
561 pointerIndex = 0;
562 mTrackingPointer = event.getPointerId(pointerIndex);
563 }
Jorim Jaggia6310292014-04-16 14:11:52 +0200564 final float x = event.getX(pointerIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100565 final float y = event.getY(pointerIndex);
566 boolean scrolledToBottom = isScrolledToBottom();
567
568 switch (event.getActionMasked()) {
569 case MotionEvent.ACTION_DOWN:
Jorim Jaggib690f0d2014-07-03 23:25:44 +0200570 mStatusBar.userActivity();
Selim Cinek173f2d02015-04-29 15:36:56 -0700571 mAnimatingOnDown = mHeightAnimator != null;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800572 mMinExpandHeight = 0.0f;
573 mDownTime = SystemClock.uptimeMillis();
574 if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
575 || mPeekAnimator != null) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200576 cancelHeightAnimator();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200577 cancelPeek();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +0200578 mTouchSlopExceeded = true;
Selim Cinek172e9142014-05-07 19:38:00 +0200579 return true;
580 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100581 mInitialTouchY = y;
Jorim Jaggia6310292014-04-16 14:11:52 +0200582 mInitialTouchX = x;
Selim Cinekab1dc952014-10-30 20:20:29 +0100583 mTouchStartedInEmptyArea = !isInContentBounds(x, y);
Selim Cinek08bd435a2019-07-26 11:37:14 -0700584 mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
Jorim Jaggi3857ac42014-06-27 18:01:12 +0200585 mJustPeeked = false;
Selim Cinek547a06b2014-11-27 14:06:04 +0100586 mMotionAborted = false;
Selim Cinek684a4422015-04-15 16:18:39 -0700587 mPanelClosedOnDown = isFullyCollapsed();
Selim Cinek79d79c42015-05-21 16:14:45 -0700588 mCollapsedAndHeadsUpOnDown = false;
Selim Cinek31094df2014-08-14 19:28:15 +0200589 mHasLayoutedSinceDown = false;
590 mUpdateFlingOnLayout = false;
Selim Cinek19c8c702014-08-25 22:09:19 +0200591 mTouchAboveFalsingThreshold = false;
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700592 addMovement(event);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100593 break;
594 case MotionEvent.ACTION_POINTER_UP:
595 final int upPointer = event.getPointerId(event.getActionIndex());
596 if (mTrackingPointer == upPointer) {
597 // gesture is ongoing, find a new pointer to track
598 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
599 mTrackingPointer = event.getPointerId(newIndex);
Jorim Jaggia6310292014-04-16 14:11:52 +0200600 mInitialTouchX = event.getX(newIndex);
601 mInitialTouchY = event.getY(newIndex);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100602 }
603 break;
Selim Cinek547a06b2014-11-27 14:06:04 +0100604 case MotionEvent.ACTION_POINTER_DOWN:
Jason Monk1fd3fc32018-08-14 17:20:09 -0400605 if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
Selim Cinek547a06b2014-11-27 14:06:04 +0100606 mMotionAborted = true;
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700607 mVelocityTracker.clear();
Selim Cinek547a06b2014-11-27 14:06:04 +0100608 }
609 break;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100610 case MotionEvent.ACTION_MOVE:
611 final float h = y - mInitialTouchY;
Siarhei Vishniakouc8548552018-10-24 23:14:55 -0700612 addMovement(event);
Selim Cinek173f2d02015-04-29 15:36:56 -0700613 if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
614 float hAbs = Math.abs(h);
615 if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
616 && hAbs > Math.abs(x - mInitialTouchX)) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200617 cancelHeightAnimator();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700618 startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100619 return true;
620 }
621 }
622 break;
Selim Cinek31094df2014-08-14 19:28:15 +0200623 case MotionEvent.ACTION_CANCEL:
624 case MotionEvent.ACTION_UP:
Siarhei Vishniakoubf33d192018-08-28 14:42:23 -0700625 mVelocityTracker.clear();
Selim Cinek31094df2014-08-14 19:28:15 +0200626 break;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100627 }
628 return false;
629 }
630
Selim Cinekab1dc952014-10-30 20:20:29 +0100631 /**
632 * @return Whether a pair of coordinates are inside the visible view content bounds.
633 */
634 protected abstract boolean isInContentBounds(float x, float y);
635
Selim Cinek831941f2015-06-17 15:09:30 -0700636 protected void cancelHeightAnimator() {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200637 if (mHeightAnimator != null) {
Selim Cinekd18b86f2017-06-21 09:44:52 -0700638 if (mHeightAnimator.isRunning()) {
639 mPanelUpdateWhenAnimatorEnds = false;
640 }
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200641 mHeightAnimator.cancel();
642 }
643 endClosing();
644 }
645
646 private void endClosing() {
647 if (mClosing) {
648 mClosing = false;
649 onClosingFinished();
650 }
651 }
652
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100653 protected boolean isScrolledToBottom() {
Selim Cinek172e9142014-05-07 19:38:00 +0200654 return true;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100655 }
656
657 protected float getContentHeight() {
658 return mExpandedHeight;
Daniel Sandlerbf526d12012-09-04 22:56:44 -0400659 }
660
Daniel Sandler08d05e32012-08-08 16:39:54 -0400661 @Override
662 protected void onFinishInflate() {
663 super.onFinishInflate();
664 loadDimens();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400665 }
666
Jorim Jaggi069cd032014-05-15 03:09:01 +0200667 @Override
668 protected void onConfigurationChanged(Configuration newConfig) {
669 super.onConfigurationChanged(newConfig);
670 loadDimens();
Jorim Jaggi069cd032014-05-15 03:09:01 +0200671 }
672
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200673 /**
Jorim Jaggib7240132014-06-30 01:39:07 +0200674 * @param vel the current vertical velocity of the motion
675 * @param vectorVel the length of the vectorial velocity
Jorim Jaggie29b2db2014-05-30 23:17:03 +0200676 * @return whether a fling should expands the panel; contracts otherwise
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200677 */
Selim Cinek9db71052015-04-24 18:54:30 -0700678 protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
Dave Mankoffc88d6222018-10-25 15:31:20 -0400679 if (mFalsingManager.isUnlockingDisabled()) {
680 return true;
681 }
682
Selim Cinek9db71052015-04-24 18:54:30 -0700683 if (isFalseTouch(x, y)) {
Selim Cinek19c8c702014-08-25 22:09:19 +0200684 return true;
685 }
Jorim Jaggib7240132014-06-30 01:39:07 +0200686 if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
Selim Cinek08bd435a2019-07-26 11:37:14 -0700687 return shouldExpandWhenNotFlinging();
Selim Cinek1685e632014-04-08 02:27:49 +0200688 } else {
Jorim Jaggie29b2db2014-05-30 23:17:03 +0200689 return vel > 0;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400690 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200691 }
692
Selim Cinek08bd435a2019-07-26 11:37:14 -0700693 protected boolean shouldExpandWhenNotFlinging() {
694 return getExpandedFraction() > 0.5f;
695 }
696
Selim Cinek9db71052015-04-24 18:54:30 -0700697 /**
698 * @param x the final x-coordinate when the finger was lifted
699 * @param y the final y-coordinate when the finger was lifted
700 * @return whether this motion should be regarded as a false touch
701 */
702 private boolean isFalseTouch(float x, float y) {
703 if (!mStatusBar.isFalsingThresholdNeeded()) {
704 return false;
705 }
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700706 if (mFalsingManager.isClassiferEnabled()) {
707 return mFalsingManager.isFalseTouch();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700708 }
Selim Cinek9db71052015-04-24 18:54:30 -0700709 if (!mTouchAboveFalsingThreshold) {
710 return true;
711 }
712 if (mUpwardsWhenTresholdReached) {
713 return false;
714 }
715 return !isDirectionUpwards(x, y);
Selim Cinek5386fb32014-09-03 16:37:36 +0200716 }
717
Jorim Jaggi1d480692014-05-20 19:41:58 +0200718 protected void fling(float vel, boolean expand) {
Selim Cinek9db71052015-04-24 18:54:30 -0700719 fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700720 }
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200721
Selim Cinek9db71052015-04-24 18:54:30 -0700722 protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
723 fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
724 }
725
726 protected void fling(float vel, boolean expand, float collapseSpeedUpFactor,
727 boolean expandBecauseOfFalsing) {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700728 cancelPeek();
Selim Cinek48ff9b42016-11-09 19:31:51 -0800729 float target = expand ? getMaxPanelHeight() : 0;
Jorim Jaggi3e02adb2015-05-13 17:50:26 -0700730 if (!expand) {
731 mClosing = true;
732 }
Selim Cinek9db71052015-04-24 18:54:30 -0700733 flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700734 }
735
736 protected void flingToHeight(float vel, boolean expand, float target,
Selim Cinek9db71052015-04-24 18:54:30 -0700737 float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200738 // Hack to make the expand transition look nice when clear all button is visible - we make
739 // the animation only to the last notification, and then jump to the maximum panel height so
740 // clear all just fades in and the decelerating motion is towards the last notification.
741 final boolean clearAllExpandHack = expand && fullyExpandedClearAllVisible()
742 && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
743 && !isClearAllVisible();
744 if (clearAllExpandHack) {
745 target = getMaxPanelHeight() - getClearAllHeight();
746 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200747 if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200748 notifyExpandingFinished();
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200749 return;
750 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200751 mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
Jorim Jaggi90129582014-06-02 14:44:49 +0200752 ValueAnimator animator = createHeightAnimator(target);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200753 if (expand) {
Selim Cinek61190fb2016-12-07 12:16:45 -0800754 if (expandBecauseOfFalsing && vel < 0) {
Selim Cinek5386fb32014-09-03 16:37:36 +0200755 vel = 0;
756 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200757 mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
Jorim Jaggi6626f542016-08-22 13:08:44 -0700758 if (vel == 0) {
Selim Cinek5386fb32014-09-03 16:37:36 +0200759 animator.setDuration(350);
760 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200761 } else {
Selim Cinek2411f762016-12-28 17:05:08 +0100762 if (shouldUseDismissingAnimation()) {
Selim Cinekdf5501b2017-04-14 20:01:27 -0700763 if (vel == 0) {
764 animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
765 long duration = (long) (200 + mExpandedHeight / getHeight() * 100);
766 animator.setDuration(duration);
767 } else {
768 mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
769 getHeight());
770 }
Selim Cinek2411f762016-12-28 17:05:08 +0100771 } else {
772 mFlingAnimationUtilsClosing
773 .apply(animator, mExpandedHeight, target, vel, getHeight());
774 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200775
776 // Make it shorter if we run a canned animation
777 if (vel == 0) {
Selim Cinek593ee202016-12-28 15:01:16 +0100778 animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
Jorim Jaggi1d480692014-05-20 19:41:58 +0200779 }
Selim Cinek2627d722018-01-19 12:16:49 -0800780 if (mFixedDuration != NO_FIXED_DURATION) {
781 animator.setDuration(mFixedDuration);
782 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200783 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200784 animator.addListener(new AnimatorListenerAdapter() {
Jorim Jaggib472b3472014-06-30 19:56:24 +0200785 private boolean mCancelled;
786
787 @Override
788 public void onAnimationCancel(Animator animation) {
789 mCancelled = true;
790 }
791
Jorim Jaggi1d480692014-05-20 19:41:58 +0200792 @Override
793 public void onAnimationEnd(Animator animation) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +0200794 if (clearAllExpandHack && !mCancelled) {
Jorim Jaggi2ae259d2014-08-04 23:35:47 +0200795 setExpandedHeightInternal(getMaxPanelHeight());
796 }
Selim Cinekbe2c4432017-05-30 12:11:09 -0700797 setAnimator(null);
Jorim Jaggi2ae259d2014-08-04 23:35:47 +0200798 if (!mCancelled) {
799 notifyExpandingFinished();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200800 }
Jorim Jaggieb8f11a2015-05-20 18:42:16 -0700801 notifyBarPanelExpansionChanged();
Jorim Jaggi1d480692014-05-20 19:41:58 +0200802 }
803 });
Selim Cinekbe2c4432017-05-30 12:11:09 -0700804 setAnimator(animator);
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200805 animator.start();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400806 }
807
Selim Cinek2411f762016-12-28 17:05:08 +0100808 protected abstract boolean shouldUseDismissingAnimation();
809
Daniel Sandler08d05e32012-08-08 16:39:54 -0400810 @Override
811 protected void onAttachedToWindow() {
812 super.onAttachedToWindow();
Daniel Sandler978f8532012-08-15 15:48:16 -0400813 mViewName = getResources().getResourceName(getId());
814 }
815
816 public String getName() {
817 return mViewName;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400818 }
819
Daniel Sandler08d05e32012-08-08 16:39:54 -0400820 public void setExpandedHeight(float height) {
John Spurlock97642182013-07-29 17:58:39 -0400821 if (DEBUG) logf("setExpandedHeight(%.1f)", height);
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200822 setExpandedHeightInternal(height + getOverExpansionPixels());
Daniel Sandler08d05e32012-08-08 16:39:54 -0400823 }
824
Daniel Sandler50508132012-08-16 14:10:53 -0400825 @Override
Lucas Dupin60661a62018-04-12 10:50:13 -0700826 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Daniel Sandler50508132012-08-16 14:10:53 -0400827 super.onLayout(changed, left, top, right, bottom);
Selim Cinek5fbc6322016-01-15 17:17:58 -0800828 mStatusBar.onPanelLaidOut();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100829 requestPanelHeightUpdate();
Selim Cinek31094df2014-08-14 19:28:15 +0200830 mHasLayoutedSinceDown = true;
831 if (mUpdateFlingOnLayout) {
832 abortAnimations();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700833 fling(mUpdateFlingVelocity, true /* expands */);
Selim Cinek31094df2014-08-14 19:28:15 +0200834 mUpdateFlingOnLayout = false;
835 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100836 }
837
838 protected void requestPanelHeightUpdate() {
839 float currentMaxPanelHeight = getMaxPanelHeight();
840
Selim Cinekbe2c4432017-05-30 12:11:09 -0700841 if (isFullyCollapsed()) {
842 return;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100843 }
Selim Cinekbe2c4432017-05-30 12:11:09 -0700844
845 if (currentMaxPanelHeight == mExpandedHeight) {
846 return;
847 }
848
849 if (mPeekAnimator != null || mPeekTouching) {
850 return;
851 }
852
853 if (mTracking && !isTrackingBlocked()) {
854 return;
855 }
856
857 if (mHeightAnimator != null) {
858 mPanelUpdateWhenAnimatorEnds = true;
859 return;
860 }
861
862 setExpandedHeight(currentMaxPanelHeight);
Daniel Sandler50508132012-08-16 14:10:53 -0400863 }
864
Daniel Sandler08d05e32012-08-08 16:39:54 -0400865 public void setExpandedHeightInternal(float h) {
Jorim Jaggie05256e2016-09-08 14:58:20 -0700866 if (mExpandLatencyTracking && h != 0f) {
867 DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(mContext).onActionEnd(
868 LatencyTracker.ACTION_EXPAND_PANEL));
869 mExpandLatencyTracking = false;
870 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200871 float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
872 if (mHeightAnimator == null) {
873 float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
874 if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
875 setOverExpansion(overExpansionPixels, true /* isPixels */);
876 }
877 mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
878 } else {
879 mExpandedHeight = h;
880 if (mOverExpandedBeforeFling) {
881 setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
882 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100883 }
Daniel Sandler198a0302012-08-17 16:04:31 -0400884
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200885 // If we are closing the panel and we are almost there due to a slow decelerating
886 // interpolator, abort the animation.
887 if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
888 mExpandedHeight = 0f;
889 if (mHeightAnimator != null) {
890 mHeightAnimator.end();
891 }
892 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800893 mExpandedFraction = Math.min(1f,
894 fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100895 onHeightUpdated(mExpandedHeight);
Jorim Jaggib472b3472014-06-30 19:56:24 +0200896 notifyBarPanelExpansionChanged();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400897 }
898
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200899 /**
900 * @return true if the panel tracking should be temporarily blocked; this is used when a
901 * conflicting gesture (opening QS) is happening
902 */
903 protected abstract boolean isTrackingBlocked();
904
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200905 protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
Selim Cinek24120a52014-05-26 10:05:42 +0200906
Jorim Jaggi90129582014-06-02 14:44:49 +0200907 protected abstract void onHeightUpdated(float expandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100908
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200909 protected abstract float getOverExpansionAmount();
910
911 protected abstract float getOverExpansionPixels();
912
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100913 /**
914 * This returns the maximum height of the panel. Children should override this if their
915 * desired height is not the full height.
916 *
917 * @return the default implementation simply returns the maximum height.
918 */
Selim Cinek31094df2014-08-14 19:28:15 +0200919 protected abstract int getMaxPanelHeight();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400920
921 public void setExpandedFraction(float frac) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100922 setExpandedHeight(getMaxPanelHeight() * frac);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400923 }
924
925 public float getExpandedHeight() {
926 return mExpandedHeight;
927 }
928
929 public float getExpandedFraction() {
930 return mExpandedFraction;
931 }
932
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700933 public boolean isFullyExpanded() {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100934 return mExpandedHeight >= getMaxPanelHeight();
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700935 }
936
937 public boolean isFullyCollapsed() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800938 return mExpandedFraction <= 0.0f;
Daniel Sandler67eab792012-10-02 17:08:23 -0400939 }
940
941 public boolean isCollapsing() {
Selim Cinek7e222c3c2018-01-25 12:22:41 -0800942 return mClosing || mLaunchingNotification;
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700943 }
944
John Spurlocka4b70af2013-08-17 14:05:49 -0400945 public boolean isTracking() {
946 return mTracking;
947 }
948
Daniel Sandler08d05e32012-08-08 16:39:54 -0400949 public void setBar(PanelBar panelBar) {
950 mBar = panelBar;
951 }
952
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700953 public void collapse(boolean delayed, float speedUpFactor) {
John Spurlock97642182013-07-29 17:58:39 -0400954 if (DEBUG) logf("collapse: " + this);
Selim Cinek9bb05632016-12-15 14:14:29 -0800955 if (canPanelBeCollapsed()) {
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200956 cancelHeightAnimator();
Jorim Jaggib472b3472014-06-30 19:56:24 +0200957 notifyExpandingStarted();
Jorim Jaggi3b866be2015-06-30 11:31:10 -0700958
959 // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
960 mClosing = true;
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200961 if (delayed) {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -0700962 mNextCollapseSpeedUpFactor = speedUpFactor;
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200963 postDelayed(mFlingCollapseRunnable, 120);
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200964 } else {
Selim Cinek9db71052015-04-24 18:54:30 -0700965 fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200966 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400967 }
968 }
969
Selim Cinek9bb05632016-12-15 14:14:29 -0800970 public boolean canPanelBeCollapsed() {
971 return !isFullyCollapsed() && !mTracking && !mClosing;
972 }
973
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200974 private final Runnable mFlingCollapseRunnable = new Runnable() {
975 @Override
976 public void run() {
Selim Cinek9db71052015-04-24 18:54:30 -0700977 fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
978 false /* expandBecauseOfFalsing */);
Jorim Jaggi5cbfe542014-09-10 22:23:08 +0200979 }
980 };
981
John Spurlock97642182013-07-29 17:58:39 -0400982 public void cancelPeek() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800983 boolean cancelled = false;
Jorim Jaggib472b3472014-06-30 19:56:24 +0200984 if (mPeekAnimator != null) {
Selim Cinek529c5322016-04-06 20:03:45 -0700985 cancelled = true;
John Spurlock97642182013-07-29 17:58:39 -0400986 mPeekAnimator.cancel();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400987 }
Jorim Jaggib472b3472014-06-30 19:56:24 +0200988
Selim Cinek529c5322016-04-06 20:03:45 -0700989 if (cancelled) {
990 // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
991 // notify mBar that we might have closed ourselves.
992 notifyBarPanelExpansionChanged();
993 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400994 }
Daniel Sandler37a38aa2013-02-13 17:15:57 -0500995
Oren Blasberg8d3fea12015-07-10 14:21:44 -0700996 public void expand(final boolean animate) {
997 if (!isFullyCollapsed() && !isCollapsing()) {
998 return;
999 }
1000
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001001 mInstantExpanding = true;
Jorim Jaggi6626f542016-08-22 13:08:44 -07001002 mAnimateAfterExpanding = animate;
Jorim Jaggi5cbfe542014-09-10 22:23:08 +02001003 mUpdateFlingOnLayout = false;
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001004 abortAnimations();
Jorim Jaggi5cbfe542014-09-10 22:23:08 +02001005 cancelPeek();
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001006 if (mTracking) {
1007 onTrackingStopped(true /* expands */); // The panel is expanded after this call.
Jorim Jaggie62d5892014-09-02 16:31:19 +02001008 }
1009 if (mExpanding) {
Jorim Jaggib472b3472014-06-30 19:56:24 +02001010 notifyExpandingFinished();
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001011 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001012 notifyBarPanelExpansionChanged();
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001013
1014 // Wait for window manager to pickup the change, so we know the maximum height of the panel
1015 // then.
1016 getViewTreeObserver().addOnGlobalLayoutListener(
1017 new ViewTreeObserver.OnGlobalLayoutListener() {
1018 @Override
1019 public void onGlobalLayout() {
Jorim Jaggic5804af2016-04-25 18:51:16 -07001020 if (!mInstantExpanding) {
1021 getViewTreeObserver().removeOnGlobalLayoutListener(this);
1022 return;
1023 }
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001024 if (mStatusBar.getStatusBarWindow().getHeight()
1025 != mStatusBar.getStatusBarHeight()) {
1026 getViewTreeObserver().removeOnGlobalLayoutListener(this);
Jorim Jaggi6626f542016-08-22 13:08:44 -07001027 if (mAnimateAfterExpanding) {
Oren Blasberg8d3fea12015-07-10 14:21:44 -07001028 notifyExpandingStarted();
1029 fling(0, true /* expand */);
1030 } else {
1031 setExpandedFraction(1f);
1032 }
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001033 mInstantExpanding = false;
1034 }
1035 }
1036 });
1037
1038 // Make sure a layout really happens.
1039 requestLayout();
1040 }
1041
Selim Cinek6bb4a9b2014-10-09 17:48:05 -07001042 public void instantCollapse() {
1043 abortAnimations();
1044 setExpandedFraction(0f);
1045 if (mExpanding) {
1046 notifyExpandingFinished();
1047 }
Jorim Jaggic5804af2016-04-25 18:51:16 -07001048 if (mInstantExpanding) {
1049 mInstantExpanding = false;
1050 notifyBarPanelExpansionChanged();
1051 }
Selim Cinek6bb4a9b2014-10-09 17:48:05 -07001052 }
1053
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001054 private void abortAnimations() {
1055 cancelPeek();
Selim Cinekdbbcfbe2014-10-24 17:52:35 +02001056 cancelHeightAnimator();
Jorim Jaggi5cbfe542014-09-10 22:23:08 +02001057 removeCallbacks(mPostCollapseRunnable);
1058 removeCallbacks(mFlingCollapseRunnable);
Jorim Jaggi0a27be82014-06-11 03:22:39 +02001059 }
1060
Selim Cinekdbbcfbe2014-10-24 17:52:35 +02001061 protected void onClosingFinished() {
1062 mBar.onClosingFinished();
1063 }
1064
1065
Jorim Jaggi90129582014-06-02 14:44:49 +02001066 protected void startUnlockHintAnimation() {
1067
1068 // We don't need to hint the user if an animation is already running or the user is changing
1069 // the expansion.
1070 if (mHeightAnimator != null || mTracking) {
1071 return;
1072 }
1073 cancelPeek();
Jorim Jaggib472b3472014-06-30 19:56:24 +02001074 notifyExpandingStarted();
Lucas Dupin60661a62018-04-12 10:50:13 -07001075 startUnlockHintAnimationPhase1(() -> {
1076 notifyExpandingFinished();
1077 onUnlockHintFinished();
1078 mHintAnimationRunning = false;
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001079 });
Selim Cinekec29d342017-05-05 18:31:49 -07001080 onUnlockHintStarted();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001081 mHintAnimationRunning = true;
Jorim Jaggi90129582014-06-02 14:44:49 +02001082 }
1083
Selim Cinekec29d342017-05-05 18:31:49 -07001084 protected void onUnlockHintFinished() {
1085 mStatusBar.onHintFinished();
1086 }
1087
1088 protected void onUnlockHintStarted() {
1089 mStatusBar.onUnlockHintStarted();
1090 }
1091
Lucas Dupinbc9aac12018-03-04 20:18:15 -08001092 public boolean isUnlockHintRunning() {
1093 return mHintAnimationRunning;
1094 }
1095
Jorim Jaggi90129582014-06-02 14:44:49 +02001096 /**
1097 * Phase 1: Move everything upwards.
1098 */
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001099 private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
Jorim Jaggi90129582014-06-02 14:44:49 +02001100 float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
1101 ValueAnimator animator = createHeightAnimator(target);
1102 animator.setDuration(250);
Selim Cinekc18010f2016-01-20 13:41:30 -08001103 animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
Jorim Jaggi90129582014-06-02 14:44:49 +02001104 animator.addListener(new AnimatorListenerAdapter() {
1105 private boolean mCancelled;
1106
1107 @Override
1108 public void onAnimationCancel(Animator animation) {
1109 mCancelled = true;
1110 }
1111
1112 @Override
1113 public void onAnimationEnd(Animator animation) {
1114 if (mCancelled) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07001115 setAnimator(null);
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001116 onAnimationFinished.run();
Jorim Jaggi90129582014-06-02 14:44:49 +02001117 } else {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001118 startUnlockHintAnimationPhase2(onAnimationFinished);
Jorim Jaggi90129582014-06-02 14:44:49 +02001119 }
1120 }
1121 });
1122 animator.start();
Selim Cinekbe2c4432017-05-30 12:11:09 -07001123 setAnimator(animator);
Adrian Roos8d4e99f2017-05-25 18:03:58 -07001124
1125 View[] viewsToAnimate = {
1126 mKeyguardBottomArea.getIndicationArea(),
1127 mStatusBar.getAmbientIndicationContainer()};
1128 for (View v : viewsToAnimate) {
1129 if (v == null) {
1130 continue;
1131 }
1132 v.animate()
1133 .translationY(-mHintDistance)
1134 .setDuration(250)
1135 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
1136 .withEndAction(() -> v.animate()
1137 .translationY(0)
1138 .setDuration(450)
1139 .setInterpolator(mBounceInterpolator)
1140 .start())
1141 .start();
1142 }
Jorim Jaggi90129582014-06-02 14:44:49 +02001143 }
1144
Selim Cinekbe2c4432017-05-30 12:11:09 -07001145 private void setAnimator(ValueAnimator animator) {
1146 mHeightAnimator = animator;
1147 if (animator == null && mPanelUpdateWhenAnimatorEnds) {
1148 mPanelUpdateWhenAnimatorEnds = false;
1149 requestPanelHeightUpdate();
1150 }
1151 }
1152
Jorim Jaggi90129582014-06-02 14:44:49 +02001153 /**
1154 * Phase 2: Bounce down.
1155 */
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001156 private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
Jorim Jaggi90129582014-06-02 14:44:49 +02001157 ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
1158 animator.setDuration(450);
1159 animator.setInterpolator(mBounceInterpolator);
1160 animator.addListener(new AnimatorListenerAdapter() {
1161 @Override
1162 public void onAnimationEnd(Animator animation) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07001163 setAnimator(null);
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001164 onAnimationFinished.run();
Jorim Jaggieb8f11a2015-05-20 18:42:16 -07001165 notifyBarPanelExpansionChanged();
Jorim Jaggi90129582014-06-02 14:44:49 +02001166 }
1167 });
1168 animator.start();
Selim Cinekbe2c4432017-05-30 12:11:09 -07001169 setAnimator(animator);
Jorim Jaggi90129582014-06-02 14:44:49 +02001170 }
1171
1172 private ValueAnimator createHeightAnimator(float targetHeight) {
1173 ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
Lucas Dupin2e838ac2019-04-17 16:50:58 -07001174 animator.addUpdateListener(
1175 animation -> setExpandedHeightInternal((float) animation.getAnimatedValue()));
Jorim Jaggi90129582014-06-02 14:44:49 +02001176 return animator;
1177 }
1178
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001179 protected void notifyBarPanelExpansionChanged() {
Evan Laird0de26b02019-03-01 15:12:50 -05001180 if (mBar != null) {
Aran Inkb6aadb42019-05-16 14:22:12 -04001181 mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f
Evan Laird0de26b02019-03-01 15:12:50 -05001182 || mPeekAnimator != null || mInstantExpanding
1183 || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null);
1184 }
Selim Cineka678b0e2019-07-23 13:12:55 -07001185 for (int i = 0; i < mExpansionListeners.size(); i++) {
1186 mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
Lucas Dupinbc9aac12018-03-04 20:18:15 -08001187 }
1188 }
1189
Selim Cineka678b0e2019-07-23 13:12:55 -07001190 public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
1191 mExpansionListeners.add(panelExpansionListener);
Jorim Jaggib472b3472014-06-30 19:56:24 +02001192 }
1193
Selim Cinek0fccc722015-07-29 17:04:36 -07001194 protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
1195
Jorim Jaggi90129582014-06-02 14:44:49 +02001196 /**
1197 * Gets called when the user performs a click anywhere in the empty area of the panel.
1198 *
1199 * @return whether the panel will be expanded after the action performed by this method
1200 */
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001201 protected boolean onEmptySpaceClick(float x) {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001202 if (mHintAnimationRunning) {
1203 return true;
1204 }
Selim Cinek6746c282015-04-21 19:58:31 -07001205 return onMiddleClicked();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001206 }
1207
Jorim Jaggi1cf6e102015-01-20 16:45:05 +01001208 protected final Runnable mPostCollapseRunnable = new Runnable() {
Jorim Jaggi488b7922014-08-05 21:12:02 +02001209 @Override
1210 public void run() {
Jorim Jaggif3b3bee2015-04-16 14:57:34 -07001211 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
Jorim Jaggi488b7922014-08-05 21:12:02 +02001212 }
1213 };
Jorim Jaggi488b7922014-08-05 21:12:02 +02001214
Jorim Jaggi1cf6e102015-01-20 16:45:05 +01001215 protected abstract boolean onMiddleClicked();
Jorim Jaggi90129582014-06-02 14:44:49 +02001216
Jorim Jaggid7912d22014-09-30 17:38:19 +02001217 protected abstract boolean isDozing();
1218
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001219 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Selim Cineka3e5bcb2014-04-07 20:07:22 +02001220 pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
Jorim Jaggi50d87a72014-09-12 21:43:35 +02001221 + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s touchDisabled=%s"
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001222 + "]",
1223 this.getClass().getSimpleName(),
1224 getExpandedHeight(),
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001225 getMaxPanelHeight(),
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001226 mClosing?"T":"f",
1227 mTracking?"T":"f",
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001228 mJustPeeked?"T":"f",
1229 mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""),
Jorim Jaggi50d87a72014-09-12 21:43:35 +02001230 mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":""),
1231 mTouchDisabled?"T":"f"
Daniel Sandler37a38aa2013-02-13 17:15:57 -05001232 ));
1233 }
Selim Cinek3c4635c2014-05-29 02:12:47 +02001234
Lucas Dupinee63c372018-08-03 11:58:24 -07001235 public abstract void resetViews(boolean animate);
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001236
1237 protected abstract float getPeekHeight();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02001238 /**
1239 * @return whether "Clear all" button will be visible when the panel is fully expanded
1240 */
1241 protected abstract boolean fullyExpandedClearAllVisible();
1242
1243 protected abstract boolean isClearAllVisible();
1244
1245 /**
1246 * @return the height of the clear all button, in pixels
1247 */
1248 protected abstract int getClearAllHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001249
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001250 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001251 mHeadsUpManager = headsUpManager;
1252 }
Selim Cinek2627d722018-01-19 12:16:49 -08001253
1254 public void setLaunchingNotification(boolean launchingNotification) {
1255 mLaunchingNotification = launchingNotification;
1256 }
1257
1258 public void collapseWithDuration(int animationDuration) {
1259 mFixedDuration = animationDuration;
1260 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
1261 mFixedDuration = NO_FIXED_DURATION;
1262 }
Daniel Sandler08d05e32012-08-08 16:39:54 -04001263}