blob: ad8a0eb98ead58bfce2eb31fceaf165cebb30122 [file] [log] [blame]
Selim Cinek67b22602014-03-10 15:40:16 +01001/*
2 * Copyright (C) 2014 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
17package com.android.systemui.statusbar.stack;
18
Selim Cinek2627d722018-01-19 12:16:49 -080019import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
20
Selim Cinek614576e2016-01-20 10:54:09 -080021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.ObjectAnimator;
24import android.animation.PropertyValuesHolder;
Selim Cinekd35c2792016-01-21 13:20:57 -080025import android.animation.TimeAnimator;
26import android.animation.ValueAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080027import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinek31d37b92016-04-26 09:56:42 -070028import android.annotation.FloatRange;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010029import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010030import android.content.Context;
31import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080032import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010033import android.graphics.Canvas;
Lucas Dupind285cf02018-01-18 09:18:23 -080034import android.graphics.Color;
Selim Cinek67b22602014-03-10 15:40:16 +010035import android.graphics.Paint;
Selim Cinek515b2032017-11-15 10:20:19 -080036import android.graphics.Path;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010037import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080038import android.graphics.PorterDuff;
39import android.graphics.PorterDuffXfermode;
40import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070041import android.os.Bundle;
Mady Mellor4b80b102016-01-22 08:03:58 -080042import android.os.Handler;
Mady Mellor95d743c2017-01-10 12:05:27 -080043import android.service.notification.StatusBarNotification;
Anthony Chen5e3742e2017-04-07 14:28:44 -070044import android.support.annotation.NonNull;
Selim Cinek8a9308d2017-08-24 09:31:08 -070045import android.support.annotation.VisibleForTesting;
Selim Cinek67b22602014-03-10 15:40:16 +010046import android.util.AttributeSet;
Selim Cinek972123d2016-05-03 14:25:58 -070047import android.util.FloatProperty;
Selim Cinek67b22602014-03-10 15:40:16 +010048import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080049import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070050import android.util.Pair;
Selim Cinek972123d2016-05-03 14:25:58 -070051import android.util.Property;
Lucas Dupine17ce522017-07-17 15:45:06 -070052import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070053import android.view.InputDevice;
Selim Cinek67b22602014-03-10 15:40:16 +010054import android.view.MotionEvent;
55import android.view.VelocityTracker;
56import android.view.View;
57import android.view.ViewConfiguration;
58import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020059import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070060import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070061import android.view.accessibility.AccessibilityEvent;
62import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020063import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080064import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010065import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070066import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070067
Mady Mellora41587b2016-02-11 18:43:06 -080068import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010069import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Lucas Dupine17ce522017-07-17 15:45:06 -070070import com.android.settingslib.Utils;
Selim Cinek67b22602014-03-10 15:40:16 +010071import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080072import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010073import com.android.systemui.R;
74import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070075import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080076import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
77import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
78import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Selim Cineka32ab602014-06-11 15:06:01 +020079import com.android.systemui.statusbar.ActivatableNotificationView;
Dan Sandlereceda3d2014-07-21 15:35:01 -040080import com.android.systemui.statusbar.DismissView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020081import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010082import com.android.systemui.statusbar.ExpandableNotificationRow;
Jorim Jaggibe565df2014-04-28 17:51:23 +020083import com.android.systemui.statusbar.ExpandableView;
Adrian Roos7d062c42017-03-30 15:11:43 -070084import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070085import com.android.systemui.statusbar.NotificationGuts;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090086import com.android.systemui.statusbar.NotificationListContainer;
87import com.android.systemui.statusbar.NotificationLogger;
Selim Cinek0cfbef42016-11-09 19:06:36 -080088import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070089import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010090import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020091import com.android.systemui.statusbar.StatusBarState;
Selim Cinek33223572016-02-19 19:32:22 -080092import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080093import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080094import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Selim Cinekb5605e52015-02-20 18:21:41 +010095import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050096import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinekaac93252015-04-14 20:04:12 -070097import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070098import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010099import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +0100100
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700101import android.support.v4.graphics.ColorUtils;
102
Selim Cinek707e2072017-06-30 18:32:40 +0200103import java.io.FileDescriptor;
104import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200105import java.util.ArrayList;
Adrian Roos7d062c42017-03-30 15:11:43 -0700106import java.util.Collection;
Selim Cinek33223572016-02-19 19:32:22 -0800107import java.util.Collections;
108import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200109import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800110import java.util.List;
Selim Cinek572bbd42014-04-25 16:43:27 +0200111
Selim Cinek67b22602014-03-10 15:40:16 +0100112/**
113 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
114 */
115public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200116 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800117 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900118 NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
119 NotificationListContainer {
Selim Cinek67b22602014-03-10 15:40:16 +0100120
Selim Cinekd35c2792016-01-21 13:20:57 -0800121 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800122 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100123 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200124 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
125 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200126 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100127 /**
128 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
129 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800130 private static final int INVALID_POINTER = -1;
Selim Cinek67b22602014-03-10 15:40:16 +0100131
Selim Cinek1408eb52014-06-02 14:45:38 +0200132 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800133 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200134 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100135 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800136 private final Paint mBackgroundPaint = new Paint();
Selim Cinek515b2032017-11-15 10:20:19 -0800137 private final Path mBackgroundPath = new Path();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700138 private final boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100139
Selim Cinekbc243a92016-09-27 16:35:13 -0700140 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100141 private int mOwnScrollY;
142 private int mMaxLayoutHeight;
143
144 private VelocityTracker mVelocityTracker;
145 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100146 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100147 private int mTouchSlop;
148 private int mMinimumVelocity;
149 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100150 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200151 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100152 private boolean mIsBeingDragged;
153 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200154 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700155 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100156 private boolean mTouchIsClick;
157 private float mInitialTouchX;
158 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100159
Selim Cinek67b22602014-03-10 15:40:16 +0100160 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100161 private int mContentHeight;
162 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100163 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800164 private int mIncreasedPaddingBetweenElements;
Lucas Dupind285cf02018-01-18 09:18:23 -0800165 private int mRegularTopPadding;
166 private int mDarkTopPadding;
167 // Current padding, will be either mRegularTopPadding or mDarkTopPadding
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200168 private int mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800169 // Distance between AOD separator and shelf
170 private int mDarkSeparatorPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700171 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700172 private int mBottomInset = 0;
Selim Cinek67b22602014-03-10 15:40:16 +0100173
174 /**
175 * The algorithm which calculates the properties for our children
176 */
Muyuan Li87798022016-04-07 17:51:25 -0700177 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100178
179 /**
180 * The current State this Layout is in
181 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200182 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700183 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100184 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800185 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700186 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
187 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
188 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
189 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
190 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200191 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700192 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
193 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200194 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200195 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200196 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800197 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200198
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200199 /**
200 * The raw amount of the overScroll on the top, which is not rubber-banded.
201 */
202 private float mOverScrolledTopPixels;
203
204 /**
205 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
206 */
207 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900208 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200209 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200210 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100211 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200212 private boolean mNeedsAnimation;
213 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200214 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200215 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400216 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100217 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200218 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200219 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200220 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200221 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200222 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700223 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200224 private boolean mExpandingNotification;
225 private boolean mExpandedInThisMotion;
Muyuan Li84b45612016-06-01 11:05:08 -0700226 protected boolean mScrollingEnabled;
Muyuan Li8cf63dc2016-05-18 17:40:58 -0700227 protected DismissView mDismissView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700228 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400229 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700230 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200231
232 /**
233 * Was the scroller scrolled to the top when the down motion was observed?
234 */
235 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200236 /**
237 * The minimal amount of over scroll which is needed in order to switch to the quick settings
238 * when over scrolling on a expanded card.
239 */
240 private float mMinTopOverScrollToEscape;
241 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700242 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200243 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200244 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700245 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200246 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100247 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700248 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700249
Selim Cinek1408eb52014-06-02 14:45:38 +0200250 /**
251 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
252 * This is needed to avoid scrolling too far after the notification was collapsed in the same
253 * motion.
254 */
255 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000256 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800257
Mady Mellor95d743c2017-01-10 12:05:27 -0800258 private NotificationMenuRowPlugin mCurrMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800259 private View mTranslatingParentView;
Mady Mellor95d743c2017-01-10 12:05:27 -0800260 private View mMenuExposedView;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700261 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200262
263 /**
264 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
265 * animating.
266 */
267 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700268 private boolean mDisallowDismissInThisMotion;
Jorim Jaggi56306252014-07-03 00:40:09 +0200269 private boolean mInterceptDelegateEnabled;
270 private boolean mDelegateToScrollView;
Selim Cineka59ecc32015-04-07 10:51:49 -0700271 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700272 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200273 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200274 = new ViewTreeObserver.OnPreDrawListener() {
275 @Override
276 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400277 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200278 updateChildren();
279 mChildrenUpdateRequested = false;
280 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200281 return true;
282 }
283 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500284 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100285 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100286 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700287 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek0fccc722015-07-29 17:04:36 -0700288 private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700289 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
290 = new HashSet<>();
291 private HeadsUpManager mHeadsUpManager;
292 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700293 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200294 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700295 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700296 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800297 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700298 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800299 = new ViewTreeObserver.OnPreDrawListener() {
300 @Override
301 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700302 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800303 return true;
304 }
305 };
306 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800307 private Rect mStartAnimationRect = new Rect();
308 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800309 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800310 private boolean mAnimateNextBackgroundBottom;
311 private boolean mAnimateNextBackgroundTop;
312 private ObjectAnimator mBottomAnimator = null;
313 private ObjectAnimator mTopAnimator = null;
314 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
315 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800316 private int mBgColor;
317 private float mDimAmount;
318 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800319 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800320 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
321 @Override
322 public void onAnimationEnd(Animator animation) {
323 mDimAnimator = null;
324 }
325 };
326 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
327 = new ValueAnimator.AnimatorUpdateListener() {
328
329 @Override
330 public void onAnimationUpdate(ValueAnimator animation) {
331 setDimAmount((Float) animation.getAnimatedValue());
332 }
333 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700334 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800335 private boolean mContinuousShadowUpdate;
336 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
337 = new ViewTreeObserver.OnPreDrawListener() {
338
339 @Override
340 public boolean onPreDraw() {
341 updateViewShadows();
342 return true;
343 }
344 };
345 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
346 @Override
347 public int compare(ExpandableView view, ExpandableView otherView) {
348 float endY = view.getTranslationY() + view.getActualHeight();
349 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
350 if (endY < otherEndY) {
351 return -1;
352 } else if (endY > otherEndY) {
353 return 1;
354 } else {
355 // The two notifications end at the same location
356 return 0;
357 }
358 }
359 };
Selim Cinek25503252016-03-03 15:31:43 -0800360 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
Adrian Roos7d062c42017-03-30 15:11:43 -0700361 private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700362 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700363 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800364 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700365 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700366 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400367 private View mForcedScroll;
Lucas Dupind285cf02018-01-18 09:18:23 -0800368 private float mDarkAmount = 1.0f;
369 private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
370 new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
Selim Cinek972123d2016-05-03 14:25:58 -0700371 @Override
372 public void setValue(NotificationStackScrollLayout object, float value) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800373 object.setDarkAmount(value);
Selim Cinek972123d2016-05-03 14:25:58 -0700374 }
375
376 @Override
377 public Float get(NotificationStackScrollLayout object) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800378 return object.getDarkAmount();
Selim Cinek972123d2016-05-03 14:25:58 -0700379 }
380 };
Lucas Dupine17ce522017-07-17 15:45:06 -0700381 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700382 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700383 private boolean mForwardScrollable;
384 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700385 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700386 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800387 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700388 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800389 private boolean mNoAmbient;
390 private final Rect mClipRect = new Rect();
391 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700392 private Rect mRequestedClipBounds;
393 private boolean mInHeadsUpPinnedMode;
394 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800395 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100396 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700397 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800398 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek0fe07392017-11-09 13:26:34 -0800399 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800400 private int mSidePaddings;
Lucas Dupind285cf02018-01-18 09:18:23 -0800401 private final int mSeparatorWidth;
402 private final int mSeparatorThickness;
403 private final Rect mTmpRect = new Rect();
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800404 private int mClockBottom;
Selim Cinek67b22602014-03-10 15:40:16 +0100405
406 public NotificationStackScrollLayout(Context context) {
407 this(context, null);
408 }
409
410 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
411 this(context, attrs, 0);
412 }
413
414 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
415 this(context, attrs, defStyleAttr, 0);
416 }
417
418 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
419 int defStyleRes) {
420 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800421 Resources res = getResources();
422
Selim Cinek281c2022016-10-13 19:14:43 -0700423 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800424 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800425 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
426 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200427 mExpandHelper = new ExpandHelper(getContext(), this,
428 minHeight, maxHeight);
429 mExpandHelper.setEventSource(this);
430 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800431 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Muyuan Li333a4fc2016-04-16 17:13:46 -0700432 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100433 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800434 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800435 mShouldDrawNotificationBackground =
436 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700437 mFadeNotificationsOnDismiss =
438 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Lucas Dupind285cf02018-01-18 09:18:23 -0800439 mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
440 mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
441 mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800442
443 updateWillNotDraw();
Lucas Dupind285cf02018-01-18 09:18:23 -0800444 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100445 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100446 mDebugPaint = new Paint();
447 mDebugPaint.setColor(0xffff0000);
448 mDebugPaint.setStrokeWidth(2);
449 mDebugPaint.setStyle(Paint.Style.STROKE);
450 }
451 }
452
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900453 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800454 public NotificationSwipeActionHelper getSwipeActionHelper() {
455 return mSwipeHelper;
456 }
457
Selim Cinek67b22602014-03-10 15:40:16 +0100458 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800459 public void onMenuClicked(View view, int x, int y, MenuItem item) {
460 if (mLongPressListener == null) {
461 return;
462 }
463 if (view instanceof ExpandableNotificationRow) {
464 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800465 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
466 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800467 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800468 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800469 }
470
471 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800472 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700473 if (mTranslatingParentView != null && row == mTranslatingParentView) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800474 mMenuExposedView = null;
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700475 mTranslatingParentView = null;
476 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000477 }
478
479 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800480 public void onMenuShown(View row) {
481 mMenuExposedView = mTranslatingParentView;
482 if (row instanceof ExpandableNotificationRow) {
483 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
484 ((ExpandableNotificationRow) row).getStatusBarNotification()
485 .getPackageName());
486 }
487 mSwipeHelper.onMenuShown(row);
488 }
489
Selim Cinek67b22602014-03-10 15:40:16 +0100490 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800491 if (mShouldDrawNotificationBackground
492 && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
493 drawBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700494 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800495
Selim Cinek67b22602014-03-10 15:40:16 +0100496 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800497 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100498 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800499 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100500 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200501 y = getHeight() - getEmptyBottomMargin();
502 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100503 }
504 }
505
Lucas Dupind285cf02018-01-18 09:18:23 -0800506 private void drawBackground(Canvas canvas) {
507 final int lockScreenLeft = mSidePaddings;
508 final int lockScreenRight = getWidth() - mSidePaddings;
509 final int lockScreenTop = mCurrentBounds.top;
510 final int lockScreenBottom = mCurrentBounds.bottom;
511 final int darkLeft = getWidth() / 2 - mSeparatorWidth / 2;
512 final int darkRight = darkLeft + mSeparatorWidth;
513 final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
514 final int darkBottom = darkTop + mSeparatorThickness;
515
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800516 if (mAmbientState.hasPulsingNotifications()) {
517 // TODO draw divider between notification and shelf
518 } else if (mAmbientState.isDark()) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800519 // Only draw divider on AOD if we actually have notifications
520 if (mFirstVisibleBackgroundChild != null) {
521 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
522 }
523 setClipBounds(null);
524 } else {
525 float animProgress = Interpolators.FAST_OUT_SLOW_IN
526 .getInterpolation(mDarkAmount);
527 float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
528 .getInterpolation(mDarkAmount * 2);
529 mTmpRect.set((int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
530 (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
531 (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
532 (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress));
533 canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
534 mCornerRadius, mCornerRadius, mBackgroundPaint);
535 setClipBounds(animProgress == 1 ? null : mTmpRect);
536 }
537 }
538
Selim Cinekd35c2792016-01-21 13:20:57 -0800539 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800540 // No need to update the background color if it's not being drawn.
541 if (!mShouldDrawNotificationBackground) {
542 return;
543 }
544
Lucas Dupind285cf02018-01-18 09:18:23 -0800545 final int color;
546 if (mAmbientState.isDark()) {
547 color = Color.WHITE;
548 } else {
549 float alpha =
550 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
551 alpha *= mDarkAmount;
552 // We need to manually blend in the background color
553 int scrimColor = mScrimController.getBackgroundColor();
554 color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
555 }
556
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100557 if (mCachedBackgroundColor != color) {
558 mCachedBackgroundColor = color;
559 mBackgroundPaint.setColor(color);
560 invalidate();
561 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800562 }
563
Selim Cinek67b22602014-03-10 15:40:16 +0100564 private void initView(Context context) {
565 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100566 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200567 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100568 final ViewConfiguration configuration = ViewConfiguration.get(context);
569 mTouchSlop = configuration.getScaledTouchSlop();
570 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
571 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100572 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700573
574 Resources res = context.getResources();
575 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800576 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700577 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700578 mPaddingBetweenElements = Math.max(1,
579 res.getDimensionPixelSize(R.dimen.notification_divider_height));
580 mIncreasedPaddingBetweenElements =
581 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
582 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200583 R.dimen.min_top_overscroll_to_qs);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700584 mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
585 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800586 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700587 mMinInteractionHeight = res.getDimensionPixelSize(
588 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800589 mCornerRadius = res.getDimensionPixelSize(
590 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cineka5eaa602014-05-12 21:27:47 +0200591 }
592
Selim Cinek25503252016-03-03 15:31:43 -0800593 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700594 mDrawBackgroundAsSrc = asSrc;
595 updateSrcDrawing();
596 }
597
598 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800599 if (!mShouldDrawNotificationBackground) {
600 return;
601 }
602
Selim Cinek48ff9b42016-11-09 19:31:51 -0800603 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700604 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800605 invalidate();
606 }
607
Selim Cinekaef92ef2014-06-06 18:06:04 +0200608 private void notifyHeightChangeListener(ExpandableView view) {
609 if (mOnHeightChangedListener != null) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100610 mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200611 }
Selim Cinek67b22602014-03-10 15:40:16 +0100612 }
613
614 @Override
615 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
616 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -0800617
618 int width = MeasureSpec.getSize(widthMeasureSpec);
619 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
620 MeasureSpec.getMode(widthMeasureSpec));
Selim Cinekfa760d42016-05-10 15:50:53 -0400621 // We need to measure all children even the GONE ones, such that the heights are calculated
622 // correctly as they are used to calculate how many we can fit on the screen.
623 final int size = getChildCount();
624 for (int i = 0; i < size; i++) {
Selim Cinekb95fd182017-12-21 13:03:32 -0800625 measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400626 }
Selim Cinek67b22602014-03-10 15:40:16 +0100627 }
628
629 @Override
630 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100631 // we layout all our children centered on the top
632 float centerX = getWidth() / 2.0f;
633 for (int i = 0; i < getChildCount(); i++) {
634 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400635 // We need to layout all children even the GONE ones, such that the heights are
636 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100637 float width = child.getMeasuredWidth();
638 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100639 child.layout((int) (centerX - width / 2.0f),
640 0,
641 (int) (centerX + width / 2.0f),
642 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100643 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200644 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100645 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200646 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200647 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800648 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700649 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100650 }
651
Selim Cinek5bc852a2015-12-21 12:19:09 -0800652 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
653 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200654 mNeedViewResizeAnimation = true;
655 mNeedsAnimation = true;
656 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200657 }
658
Selim Cinekdb167372016-11-17 15:41:17 -0800659 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
660 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800661 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200662 }
663
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900664 @Override
665 public void setChildLocationsChangedListener(
666 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200667 mListener = listener;
668 }
669
Selim Cineka7d4f822016-12-06 14:34:47 -0800670 @Override
671 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
672 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200673 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800674 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200675 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100676 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800677 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100678 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800679 if (row.getVisibility() != View.VISIBLE) {
680 return false;
681 }
682 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200683 }
684
Selim Cinek67b22602014-03-10 15:40:16 +0100685 private void setMaxLayoutHeight(int maxLayoutHeight) {
686 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700687 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200688 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100689 }
690
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200691 private void updateAlgorithmHeightAndPadding() {
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800692 if (mPulsing != null) {
693 mTopPadding = mClockBottom;
694 } else {
695 mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
696 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700697 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700698 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700699 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100700 }
701
Selim Cinekbc243a92016-09-27 16:35:13 -0700702 private void updateAlgorithmLayoutMinHeight() {
703 mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
704 }
705
Selim Cinek67b22602014-03-10 15:40:16 +0100706 /**
707 * Updates the children views according to the stack scroll algorithm. Call this whenever
708 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
709 */
710 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800711 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800712 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
713 ? 0
714 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200715 mAmbientState.setScrollY(mOwnScrollY);
716 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200717 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200718 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100719 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200720 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100721 }
722 }
723
Selim Cinekc383fd02016-10-21 15:31:26 -0700724 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700725 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800726 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -0700727 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
728 updateBackground();
729 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700730 }
731
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800732 private void updateClippingToTopRoundedCorner() {
Selim Cinek2627d722018-01-19 12:16:49 -0800733 Float clipStart = (float) mTopPadding + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800734 Float clipEnd = clipStart + mCornerRadius;
735 boolean first = true;
736 for (int i = 0; i < getChildCount(); i++) {
737 ExpandableView child = (ExpandableView) getChildAt(i);
738 if (child.getVisibility() == GONE) {
739 continue;
740 }
741 float start = child.getTranslationY();
742 float end = start + Math.max(child.getActualHeight() - child.getClipBottomAmount(),
743 0);
744 boolean clip = clipStart > start && clipStart < end
745 || clipEnd >= start && clipEnd <= end;
746 clip &= !(first && mOwnScrollY == 0);
747 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : -1);
748 first = false;
749 }
750 }
751
Selim Cinek3776fe02016-02-04 13:32:43 -0800752 private void updateScrollStateForAddedChildren() {
753 if (mChildrenToAddAnimated.isEmpty()) {
754 return;
755 }
756 for (int i = 0; i < getChildCount(); i++) {
757 ExpandableView child = (ExpandableView) getChildAt(i);
758 if (mChildrenToAddAnimated.contains(child)) {
759 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800760 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
761 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
762 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800763 int childHeight = getIntrinsicHeight(child) + padding;
764 if (startingPosition < mOwnScrollY) {
765 // This child starts off screen, so let's keep it offscreen to keep the others visible
766
Selim Cinekef406062016-09-29 17:33:13 -0700767 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800768 }
769 }
770 }
771 clampScrollPosition();
772 }
773
Adrian Roos181385c2016-05-05 17:45:44 -0400774 private void updateForcedScroll() {
775 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
776 || !mForcedScroll.isAttachedToWindow())) {
777 mForcedScroll = null;
778 }
779 if (mForcedScroll != null) {
780 ExpandableView expandableView = (ExpandableView) mForcedScroll;
781 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
782 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700783 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400784
785 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700786
787 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
788 // that it is not visible anymore.
789 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700790 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400791 }
792 }
793 }
794
Selim Cinek319bdc42014-05-01 23:01:58 +0200795 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200796 if (!mChildrenUpdateRequested) {
797 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
798 mChildrenUpdateRequested = true;
799 invalidate();
800 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200801 }
802
Selim Cinek67b22602014-03-10 15:40:16 +0100803 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200804 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100805 }
806
Selim Cinekf7a14c02014-07-07 14:01:46 +0200807 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100808 int scrollRange = getScrollRange();
809 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700810 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100811 }
812 }
813
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200814 public int getTopPadding() {
815 return mTopPadding;
816 }
817
Selim Cinek1408eb52014-06-02 14:45:38 +0200818 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800819 if (mRegularTopPadding != topPadding) {
820 mRegularTopPadding = topPadding;
821 mDarkTopPadding = topPadding + mDarkSeparatorPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200822 updateAlgorithmHeightAndPadding();
823 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200824 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200825 mTopPaddingNeedsAnimation = true;
826 mNeedsAnimation = true;
827 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200828 requestChildrenUpdate();
Selim Cinekaef92ef2014-06-06 18:06:04 +0200829 notifyHeightChangeListener(null);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200830 }
831 }
832
833 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700834 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200835 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700836 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200837 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700838 public void setExpandedHeight(float height) {
839 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700840 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800841 int minExpansionHeight = getMinExpansionHeight();
842 if (height < minExpansionHeight) {
843 mClipRect.left = 0;
844 mClipRect.right = getWidth();
845 mClipRect.top = 0;
846 mClipRect.bottom = (int) height;
847 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700848 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800849 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700850 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800851 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200852 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700853 float translationY;
854 float appearEndPosition = getAppearEndPosition();
855 float appearStartPosition = getAppearStartPosition();
856 if (height >= appearEndPosition) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700857 translationY = 0;
Selim Cinek94c2d822016-07-13 18:50:04 -0700858 stackHeight = (int) height;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200859 } else {
Selim Cinek94c2d822016-07-13 18:50:04 -0700860 float appearFraction = getAppearFraction(height);
861 if (appearFraction >= 0) {
862 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
863 appearFraction);
864 } else {
865 // This may happen when pushing up a heads up. We linearly push it up from the
866 // start
867 translationY = height - appearStartPosition + getExpandTranslationStart();
868 }
869 stackHeight = (int) (height - translationY);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200870 }
871 if (stackHeight != mCurrentStackHeight) {
872 mCurrentStackHeight = stackHeight;
873 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200874 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200875 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700876 setStackTranslation(translationY);
Selim Cinekcafa87f2016-10-26 17:00:17 -0700877 }
878
879 private void setRequestedClipBounds(Rect clipRect) {
880 mRequestedClipBounds = clipRect;
881 updateClipping();
882 }
883
884 public void updateClipping() {
885 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
886 && !mHeadsUpAnimatingAway;
887 if (mIsClipped != clipped) {
888 mIsClipped = clipped;
889 updateFadingState();
890 }
891 if (clipped) {
892 setClipBounds(mRequestedClipBounds);
893 } else {
894 setClipBounds(null);
895 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700896 }
897
898 /**
899 * @return The translation at the beginning when expanding.
900 * Measured relative to the resting position.
901 */
902 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800903 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700904 }
905
906 /**
907 * @return the position from where the appear transition starts when expanding.
908 * Measured in absolute height.
909 */
910 private float getAppearStartPosition() {
Selim Cinekd127d792016-11-01 19:11:41 -0700911 if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
Selim Cinekebf42342017-07-13 15:46:10 +0200912 if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) {
Selim Cinekd127d792016-11-01 19:11:41 -0700913 // If we ever expanded beyond the first notification, it's allowed to merge into
914 // the shelf
915 return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
916 }
917 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800918 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700919 }
920
921 /**
922 * @return the position from where the appear transition ends when expanding.
923 * Measured in absolute height.
924 */
925 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -0700926 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -0700927 int notGoneChildCount = getNotGoneChildCount();
928 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100929 int minNotificationsForShelf = 1;
Selim Cinekebf42342017-07-13 15:46:10 +0200930 if (mTrackingHeadsUp
931 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100932 appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
933 minNotificationsForShelf = 2;
934 } else {
935 appearPosition = 0;
936 }
Selim Cinek66440cf2017-05-26 13:48:47 -0700937 if (notGoneChildCount >= minNotificationsForShelf
Anthony Chen9e05d462017-04-07 10:10:21 -0700938 && mShelf.getVisibility() != GONE) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100939 appearPosition += mShelf.getIntrinsicHeight();
940 }
Selim Cinekaa417da2016-10-27 18:17:08 -0700941 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +0100942 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -0700943 }
944 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -0700945 }
946
947 /**
948 * @param height the height of the panel
949 * @return the fraction of the appear animation that has been performed
950 */
951 public float getAppearFraction(float height) {
952 float appearEndPosition = getAppearEndPosition();
953 float appearStartPosition = getAppearStartPosition();
954 return (height - appearStartPosition)
955 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700956 }
957
Selim Cinekd2281152015-04-10 14:37:46 -0700958 public float getStackTranslation() {
959 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700960 }
961
Selim Cinekd2281152015-04-10 14:37:46 -0700962 private void setStackTranslation(float stackTranslation) {
963 if (stackTranslation != mStackTranslation) {
964 mStackTranslation = stackTranslation;
965 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700966 requestChildrenUpdate();
967 }
Selim Cinek67b22602014-03-10 15:40:16 +0100968 }
969
970 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100971 * Get the current height of the view. This is at most the msize of the view given by a the
Selim Cinek67b22602014-03-10 15:40:16 +0100972 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
973 *
974 * @return either the layout height or the externally defined height, whichever is smaller
975 */
Selim Cinek343e6e22014-04-11 21:23:30 +0200976 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +0100977 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
978 }
979
Selim Cinek816c8e42015-11-19 12:00:45 -0800980 public int getFirstItemMinHeight() {
981 final ExpandableView firstChild = getFirstChildNotGone();
982 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100983 }
984
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000985 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
Dan Sandler4247a5c2014-07-23 15:58:08 -0400986 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +0100987 }
988
Jason Monk16ac3772016-02-10 15:39:21 -0500989 public void setQsContainer(ViewGroup qsContainer) {
990 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +0200991 }
992
Mady Mellorb0a82462016-04-30 17:31:02 -0700993 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100994 public void onChildDismissed(View v) {
Selim Cinekd1395642016-04-28 12:22:42 -0700995 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
996 if (!row.isDismissed()) {
997 handleChildDismissed(v);
998 }
999 ViewGroup transientContainer = row.getTransientContainer();
1000 if (transientContainer != null) {
1001 transientContainer.removeTransientView(v);
1002 }
1003 }
1004
1005 private void handleChildDismissed(View v) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04001006 if (mDismissAllInProgress) {
1007 return;
1008 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001009 setSwipingInProgress(false);
Selim Cinekeb973562014-05-02 17:07:49 +02001010 if (mDragAnimPendingChildren.contains(v)) {
1011 // We start the swipe and finish it in the same frame, we don't want any animation
1012 // for the drag
1013 mDragAnimPendingChildren.remove(v);
1014 }
Selim Cinek572bbd42014-04-25 16:43:27 +02001015 mSwipedOutViews.add(v);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001016 mAmbientState.onDragFinished(v);
Selim Cinek33223572016-02-19 19:32:22 -08001017 updateContinuousShadowDrawing();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001018 if (v instanceof ExpandableNotificationRow) {
1019 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1020 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -07001021 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001022 }
1023 }
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001024 if (v instanceof ExpandableNotificationRow) {
1025 ((ExpandableNotificationRow) v).performDismiss(false /* fromAccessibility */);
1026 }
Blazej Magnowski72323322015-07-24 11:49:40 -07001027
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001028 mFalsingManager.onNotificationDismissed();
1029 if (mFalsingManager.shouldEnforceBouncer()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001030 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
Felipe Lemeee5d6302016-04-22 16:11:19 -07001031 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -07001032 }
Selim Cinekeb973562014-05-02 17:07:49 +02001033 }
1034
1035 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08001036 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001037 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -08001038 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +02001039 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001040 if (mAnimationsEnabled) {
1041 mSnappedBackChildren.add(animView);
1042 mNeedsAnimation = true;
1043 }
Selim Cinekeb973562014-05-02 17:07:49 +02001044 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +02001045 } else {
1046 // We start the swipe and snap back in the same frame, we don't want any animation
1047 mDragAnimPendingChildren.remove(animView);
1048 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001049 if (mCurrMenuRow != null && targetLeft == 0) {
1050 mCurrMenuRow.resetMenu();
1051 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08001052 }
Selim Cinek67b22602014-03-10 15:40:16 +01001053 }
1054
Adrian Roos5d9cc662014-05-28 17:08:13 +02001055 @Override
1056 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Selim Cinek131c1e22015-05-11 19:04:49 -07001057 if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
Selim Cinekaac93252015-04-14 20:04:12 -07001058 mScrimController.setTopHeadsUpDragAmount(animView,
Winsonbde852d2016-04-15 19:06:54 -07001059 Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
Selim Cinekaac93252015-04-14 20:04:12 -07001060 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001061 // Returning true prevents alpha fading.
1062 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +02001063 }
1064
Mady Mellorb0a82462016-04-30 17:31:02 -07001065 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001066 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001067 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001068 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001069 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -08001070 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -07001071 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001072 mDragAnimPendingChildren.add(v);
1073 mNeedsAnimation = true;
1074 }
Selim Cinekeb973562014-05-02 17:07:49 +02001075 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +01001076 }
1077
Selim Cinek684a4422015-04-15 16:18:39 -07001078 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001079 if (v instanceof ExpandableNotificationRow) {
1080 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001081 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001082 }
1083 return false;
1084 }
1085
1086 private boolean isHeadsUp(View v) {
1087 if (v instanceof ExpandableNotificationRow) {
1088 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1089 return row.isHeadsUp();
1090 }
1091 return false;
1092 }
1093
Mady Mellorb0a82462016-04-30 17:31:02 -07001094 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001095 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001096 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001097 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001098 }
1099
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001100 @Override
1101 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001102 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001103 }
1104
Mady Mellor4b80b102016-01-22 08:03:58 -08001105 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001106 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001107 View child = getChildAtPosition(ev.getX(), ev.getY());
1108 if (child instanceof ExpandableNotificationRow) {
1109 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1110 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001111 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001112 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001113 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001114 || (parent.getNotificationChildren().size() == 1
1115 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001116 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001117 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001118 // child notifications so we use the parent of the child. We also do the same
1119 // if we only have a single child.
1120 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001121 }
1122 }
1123 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001124 }
1125
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001126 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1127 getLocationOnScreen(mTempInt2);
1128 float localTouchY = touchY - mTempInt2[1];
1129
1130 ExpandableView closestChild = null;
1131 float minDist = Float.MAX_VALUE;
1132
1133 // find the view closest to the location, accounting for GONE views
1134 final int count = getChildCount();
1135 for (int childIdx = 0; childIdx < count; childIdx++) {
1136 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1137 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001138 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001139 continue;
1140 }
1141 float childTop = slidingChild.getTranslationY();
1142 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001143 float bottom = childTop + slidingChild.getActualHeight()
1144 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001145
1146 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1147 if (dist < minDist) {
1148 closestChild = slidingChild;
1149 minDist = dist;
1150 }
1151 }
1152 return closestChild;
1153 }
1154
Mady Mellorb0a82462016-04-30 17:31:02 -07001155 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001156 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001157 getLocationOnScreen(mTempInt2);
1158 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001159 }
1160
Mady Mellorb0a82462016-04-30 17:31:02 -07001161 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001162 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001163 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1164
1165 }
1166
1167 /**
1168 * Get the child at a certain screen location.
1169 *
1170 * @param touchX the x coordinate
1171 * @param touchY the y coordinate
1172 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1173 * @return the child at the given location.
1174 */
1175 private ExpandableView getChildAtPosition(float touchX, float touchY,
1176 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001177 // find the view under the pointer, accounting for GONE views
1178 final int count = getChildCount();
1179 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001180 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001181 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001182 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001183 continue;
1184 }
Selim Cinek89faff12014-06-19 16:29:04 -07001185 float childTop = slidingChild.getTranslationY();
1186 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001187 float bottom = childTop + slidingChild.getActualHeight()
1188 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001189
1190 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1191 // camera affordance).
1192 int left = 0;
1193 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001194
Selim Cinek34ed7c02017-09-08 15:03:12 -07001195 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001196 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001197 if (slidingChild instanceof ExpandableNotificationRow) {
1198 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001199 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Selim Cinek5bc852a2015-12-21 12:19:09 -08001200 && mHeadsUpManager.getTopEntry().entry.row != row
1201 && mGroupManager.getGroupSummary(
1202 mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
1203 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001204 continue;
1205 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001206 return row.getViewAtPosition(touchY - childTop);
1207 }
Selim Cinek67b22602014-03-10 15:40:16 +01001208 return slidingChild;
1209 }
1210 }
1211 return null;
1212 }
1213
Mady Mellorb0a82462016-04-30 17:31:02 -07001214 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001215 public boolean canChildBeExpanded(View v) {
1216 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001217 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001218 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001219 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001220 }
1221
Chris Wren6abeeb92016-05-26 14:44:38 -04001222 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001223 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001224 public void setUserExpandedChild(View v, boolean userExpanded) {
1225 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001226 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001227 if (userExpanded && onKeyguard()) {
1228 // Due to a race when locking the screen while touching, a notification may be
1229 // expanded even after we went back to keyguard. An example of this happens if
1230 // you click in the empty space while expanding a group.
1231
1232 // We also need to un-user lock it here, since otherwise the content height
1233 // calculated might be wrong. We also can't invert the two calls since
1234 // un-userlocking it will trigger a layout switch in the content view.
1235 row.setUserLocked(false);
1236 updateContentHeight();
1237 notifyHeightChangeListener(row);
1238 return;
1239 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001240 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1241 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001242 }
1243 }
1244
Mady Mellorb0a82462016-04-30 17:31:02 -07001245 @Override
1246 public void setExpansionCancelled(View v) {
1247 if (v instanceof ExpandableNotificationRow) {
1248 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1249 }
1250 }
1251
1252 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001253 public void setUserLockedChild(View v, boolean userLocked) {
1254 if (v instanceof ExpandableNotificationRow) {
1255 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1256 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001257 cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02001258 requestDisallowInterceptTouchEvent(true);
1259 }
1260
1261 @Override
1262 public void expansionStateChanged(boolean isExpanding) {
1263 mExpandingNotification = isExpanding;
1264 if (!mExpandedInThisMotion) {
1265 mMaxScrollAfterExpand = mOwnScrollY;
1266 mExpandedInThisMotion = true;
1267 }
1268 }
1269
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001270 @Override
1271 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001272 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001273 }
1274
Selim Cinek1408eb52014-06-02 14:45:38 +02001275 public void setScrollingEnabled(boolean enable) {
1276 mScrollingEnabled = enable;
1277 }
1278
Adrian Roos181385c2016-05-05 17:45:44 -04001279 public void lockScrollTo(View v) {
1280 if (mForcedScroll == v) {
1281 return;
1282 }
1283 mForcedScroll = v;
1284 scrollTo(v);
1285 }
1286
Ricky Waicd35def2016-05-03 11:07:07 +01001287 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001288 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001289 int positionInLinearLayout = getPositionInLinearLayout(v);
1290 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1291 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001292
Adrian Roos4a579672016-05-24 16:54:37 -07001293 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1294 // that it is not visible anymore.
1295 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001296 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1297 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001298 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001299 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001300 }
Ricky Waicd35def2016-05-03 11:07:07 +01001301 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001302 }
1303
Adrian Roos181385c2016-05-05 17:45:44 -04001304 /**
1305 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1306 * the IME.
1307 */
1308 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1309 return positionInLinearLayout + v.getIntrinsicHeight() +
1310 getImeInset() - getHeight() + getTopPadding();
1311 }
1312
Adrian Roos5153d4a2016-03-22 10:01:56 -07001313 @Override
1314 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001315 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001316
1317 int range = getScrollRange();
1318 if (mOwnScrollY > range) {
1319 // HACK: We're repeatedly getting staggered insets here while the IME is
1320 // animating away. To work around that we'll wait until things have settled.
1321 removeCallbacks(mReclamp);
1322 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001323 } else if (mForcedScroll != null) {
1324 // The scroll was requested before we got the actual inset - in case we need
1325 // to scroll up some more do so now.
1326 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001327 }
1328 return insets;
1329 }
1330
1331 private Runnable mReclamp = new Runnable() {
1332 @Override
1333 public void run() {
1334 int range = getScrollRange();
1335 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1336 mDontReportNextOverScroll = true;
1337 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001338 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001339 }
1340 };
1341
Selim Cinek1408eb52014-06-02 14:45:38 +02001342 public void setExpandingEnabled(boolean enable) {
1343 mExpandHelper.setEnabled(enable);
1344 }
1345
1346 private boolean isScrollingEnabled() {
1347 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001348 }
1349
Mady Mellorb0a82462016-04-30 17:31:02 -07001350 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001351 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001352 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001353 }
1354
Selim Cinek19c8c702014-08-25 22:09:19 +02001355 @Override
1356 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001357 return onKeyguard();
1358 }
1359
1360 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001361 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001362 }
1363
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001364 private void setSwipingInProgress(boolean isSwiped) {
1365 mSwipingInProgress = isSwiped;
1366 if(isSwiped) {
1367 requestDisallowInterceptTouchEvent(true);
1368 }
Selim Cinek67b22602014-03-10 15:40:16 +01001369 }
1370
1371 @Override
1372 protected void onConfigurationChanged(Configuration newConfig) {
1373 super.onConfigurationChanged(newConfig);
1374 float densityScale = getResources().getDisplayMetrics().density;
1375 mSwipeHelper.setDensityScale(densityScale);
1376 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1377 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1378 initView(getContext());
1379 }
1380
Dan Sandlereceda3d2014-07-21 15:35:01 -04001381 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001382 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1383 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001384 }
1385
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001386 @Override
Mady Mellor86889c22016-04-18 16:37:06 -07001387 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001388 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001389 // If the child is showing the notification menu snap to that
1390 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001391 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001392 }
1393
Selim Cinek67b22602014-03-10 15:40:16 +01001394 @Override
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001395 public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
1396 return this;
1397 }
1398
1399 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001400 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001401 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1402 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001403 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001404 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001405 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001406 if (isCancelOrUp) {
1407 mExpandHelper.onlyObserveMovements(false);
1408 }
1409 boolean wasExpandingBefore = mExpandingNotification;
1410 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001411 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1412 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001413 dispatchDownEventToScroller(ev);
1414 }
1415 }
Selim Cinek67b22602014-03-10 15:40:16 +01001416 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001417 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1418 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001419 scrollerWantsIt = onScrollTouch(ev);
1420 }
1421 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001422 if (!mIsBeingDragged
1423 && !mExpandingNotification
1424 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001425 && !mOnlyScrollingInThisMotion
1426 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001427 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1428 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001429
1430 // Check if we need to clear any snooze leavebehinds
Eliot Courtney47098cb2017-10-18 17:30:30 +09001431 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07001432 if (guts != null && !isTouchInView(ev, guts)
1433 && guts.getGutsContent() instanceof NotificationSnooze) {
1434 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1435 if ((ns.isExpanded() && isCancelOrUp)
1436 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1437 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1438 // clear it on the next non-horizontal swipe or expand event.
1439 checkSnoozeLeavebehind();
1440 }
1441 }
1442 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1443 mCheckForLeavebehind = true;
1444 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001445 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1446 }
1447
1448 private void dispatchDownEventToScroller(MotionEvent ev) {
1449 MotionEvent downEvent = MotionEvent.obtain(ev);
1450 downEvent.setAction(MotionEvent.ACTION_DOWN);
1451 onScrollTouch(downEvent);
1452 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001453 }
1454
Selim Cinek11e33232016-08-05 15:30:53 -07001455 @Override
1456 public boolean onGenericMotionEvent(MotionEvent event) {
1457 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1458 || mDisallowScrollingInThisMotion) {
1459 return false;
1460 }
1461 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1462 switch (event.getAction()) {
1463 case MotionEvent.ACTION_SCROLL: {
1464 if (!mIsBeingDragged) {
1465 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1466 if (vscroll != 0) {
1467 final int delta = (int) (vscroll * getVerticalScrollFactor());
1468 final int range = getScrollRange();
1469 int oldScrollY = mOwnScrollY;
1470 int newScrollY = oldScrollY - delta;
1471 if (newScrollY < 0) {
1472 newScrollY = 0;
1473 } else if (newScrollY > range) {
1474 newScrollY = range;
1475 }
1476 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001477 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001478 return true;
1479 }
1480 }
1481 }
1482 }
1483 }
1484 }
1485 return super.onGenericMotionEvent(event);
1486 }
1487
Selim Cinek67b22602014-03-10 15:40:16 +01001488 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001489 if (!isScrollingEnabled()) {
1490 return false;
1491 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001492 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001493 return false;
1494 }
Adrian Roos181385c2016-05-05 17:45:44 -04001495 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001496 initVelocityTrackerIfNotExists();
1497 mVelocityTracker.addMovement(ev);
1498
1499 final int action = ev.getAction();
1500
1501 switch (action & MotionEvent.ACTION_MASK) {
1502 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001503 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001504 return false;
1505 }
1506 boolean isBeingDragged = !mScroller.isFinished();
1507 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001508 /*
1509 * If being flinged and user touches, stop the fling. isFinished
1510 * will be false if being flinged.
1511 */
1512 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001513 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001514 }
1515
1516 // Remember where the motion event started
1517 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001518 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001519 mActivePointerId = ev.getPointerId(0);
1520 break;
1521 }
1522 case MotionEvent.ACTION_MOVE:
1523 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1524 if (activePointerIndex == -1) {
1525 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1526 break;
1527 }
1528
1529 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001530 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001531 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001532 final int xDiff = Math.abs(x - mDownX);
1533 final int yDiff = Math.abs(deltaY);
1534 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001535 setIsBeingDragged(true);
1536 if (deltaY > 0) {
1537 deltaY -= mTouchSlop;
1538 } else {
1539 deltaY += mTouchSlop;
1540 }
1541 }
1542 if (mIsBeingDragged) {
1543 // Scroll to follow the motion event
1544 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001545 int range = getScrollRange();
1546 if (mExpandedInThisMotion) {
1547 range = Math.min(range, mMaxScrollAfterExpand);
1548 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001549
1550 float scrollAmount;
1551 if (deltaY < 0) {
1552 scrollAmount = overScrollDown(deltaY);
1553 } else {
1554 scrollAmount = overScrollUp(deltaY, range);
1555 }
Selim Cinek67b22602014-03-10 15:40:16 +01001556
Selim Cinek9212de82017-02-06 16:04:28 -08001557 // Calling customOverScrollBy will call onCustomOverScrolled, which
1558 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001559 if (scrollAmount != 0.0f) {
1560 // The scrolling motion could not be compensated with the
1561 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001562 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1563 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001564 // If we're scrolling, leavebehinds should be dismissed
1565 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001566 }
Selim Cinek67b22602014-03-10 15:40:16 +01001567 }
1568 break;
1569 case MotionEvent.ACTION_UP:
1570 if (mIsBeingDragged) {
1571 final VelocityTracker velocityTracker = mVelocityTracker;
1572 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1573 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1574
Selim Cinek1408eb52014-06-02 14:45:38 +02001575 if (shouldOverScrollFling(initialVelocity)) {
1576 onOverScrollFling(true, initialVelocity);
1577 } else {
1578 if (getChildCount() > 0) {
1579 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1580 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1581 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1582 fling(-initialVelocity);
1583 } else {
1584 onOverScrollFling(false, initialVelocity);
1585 }
1586 } else {
1587 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1588 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001589 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001590 }
Selim Cinek67b22602014-03-10 15:40:16 +01001591 }
1592 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001593 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001594 mActivePointerId = INVALID_POINTER;
1595 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001596 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001597
Selim Cinek67b22602014-03-10 15:40:16 +01001598 break;
1599 case MotionEvent.ACTION_CANCEL:
1600 if (mIsBeingDragged && getChildCount() > 0) {
1601 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001602 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001603 }
1604 mActivePointerId = INVALID_POINTER;
1605 endDrag();
1606 }
1607 break;
1608 case MotionEvent.ACTION_POINTER_DOWN: {
1609 final int index = ev.getActionIndex();
1610 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001611 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001612 mActivePointerId = ev.getPointerId(index);
1613 break;
1614 }
1615 case MotionEvent.ACTION_POINTER_UP:
1616 onSecondaryPointerUp(ev);
1617 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001618 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001619 break;
1620 }
1621 return true;
1622 }
1623
Muyuan Li84b45612016-06-01 11:05:08 -07001624 protected boolean isInsideQsContainer(MotionEvent ev) {
1625 return ev.getY() < mQsContainer.getBottom();
1626 }
1627
Selim Cinek1408eb52014-06-02 14:45:38 +02001628 private void onOverScrollFling(boolean open, int initialVelocity) {
1629 if (mOverscrollTopChangedListener != null) {
1630 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1631 }
1632 mDontReportNextOverScroll = true;
1633 setOverScrollAmount(0.0f, true, false);
1634 }
1635
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001636 /**
1637 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1638 *
1639 * @param deltaY The amount to scroll upwards, has to be positive.
1640 * @return The amount of scrolling to be performed by the scroller,
1641 * not handled by the overScroll amount.
1642 */
1643 private float overScrollUp(int deltaY, int range) {
1644 deltaY = Math.max(deltaY, 0);
1645 float currentTopAmount = getCurrentOverScrollAmount(true);
1646 float newTopAmount = currentTopAmount - deltaY;
1647 if (currentTopAmount > 0) {
1648 setOverScrollAmount(newTopAmount, true /* onTop */,
1649 false /* animate */);
1650 }
1651 // Top overScroll might not grab all scrolling motion,
1652 // we have to scroll as well.
1653 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1654 float newScrollY = mOwnScrollY + scrollAmount;
1655 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001656 if (!mExpandedInThisMotion) {
1657 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1658 // We overScroll on the top
1659 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1660 false /* onTop */,
1661 false /* animate */);
1662 }
Selim Cinekef406062016-09-29 17:33:13 -07001663 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001664 scrollAmount = 0.0f;
1665 }
1666 return scrollAmount;
1667 }
1668
1669 /**
1670 * Perform a scroll downward and adapt the overscroll amounts accordingly
1671 *
1672 * @param deltaY The amount to scroll downwards, has to be negative.
1673 * @return The amount of scrolling to be performed by the scroller,
1674 * not handled by the overScroll amount.
1675 */
1676 private float overScrollDown(int deltaY) {
1677 deltaY = Math.min(deltaY, 0);
1678 float currentBottomAmount = getCurrentOverScrollAmount(false);
1679 float newBottomAmount = currentBottomAmount + deltaY;
1680 if (currentBottomAmount > 0) {
1681 setOverScrollAmount(newBottomAmount, false /* onTop */,
1682 false /* animate */);
1683 }
1684 // Bottom overScroll might not grab all scrolling motion,
1685 // we have to scroll as well.
1686 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1687 float newScrollY = mOwnScrollY + scrollAmount;
1688 if (newScrollY < 0) {
1689 float currentTopPixels = getCurrentOverScrolledPixels(true);
1690 // We overScroll on the top
1691 setOverScrolledPixels(currentTopPixels - newScrollY,
1692 true /* onTop */,
1693 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001694 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001695 scrollAmount = 0.0f;
1696 }
1697 return scrollAmount;
1698 }
1699
Selim Cinek67b22602014-03-10 15:40:16 +01001700 private void onSecondaryPointerUp(MotionEvent ev) {
1701 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1702 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1703 final int pointerId = ev.getPointerId(pointerIndex);
1704 if (pointerId == mActivePointerId) {
1705 // This was our active pointer going up. Choose a new
1706 // active pointer and adjust accordingly.
1707 // TODO: Make this decision more intelligent.
1708 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1709 mLastMotionY = (int) ev.getY(newPointerIndex);
1710 mActivePointerId = ev.getPointerId(newPointerIndex);
1711 if (mVelocityTracker != null) {
1712 mVelocityTracker.clear();
1713 }
1714 }
1715 }
1716
1717 private void initVelocityTrackerIfNotExists() {
1718 if (mVelocityTracker == null) {
1719 mVelocityTracker = VelocityTracker.obtain();
1720 }
1721 }
1722
1723 private void recycleVelocityTracker() {
1724 if (mVelocityTracker != null) {
1725 mVelocityTracker.recycle();
1726 mVelocityTracker = null;
1727 }
1728 }
1729
1730 private void initOrResetVelocityTracker() {
1731 if (mVelocityTracker == null) {
1732 mVelocityTracker = VelocityTracker.obtain();
1733 } else {
1734 mVelocityTracker.clear();
1735 }
1736 }
1737
Ricky Waicd35def2016-05-03 11:07:07 +01001738 public void setFinishScrollingCallback(Runnable runnable) {
1739 mFinishScrollingCallback = runnable;
1740 }
1741
Selim Cinek9212de82017-02-06 16:04:28 -08001742 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001743 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001744 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001745 int y = mScroller.getCurrY();
1746
Selim Cinek9212de82017-02-06 16:04:28 -08001747 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001748 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001749 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1750 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001751 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001752 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1753 }
1754 }
Selim Cinek67b22602014-03-10 15:40:16 +01001755
Adrian Roos5153d4a2016-03-22 10:01:56 -07001756 if (mDontClampNextScroll) {
1757 range = Math.max(range, oldY);
1758 }
Selim Cinek9212de82017-02-06 16:04:28 -08001759 customOverScrollBy(y - oldY, oldY, range,
1760 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001761 }
1762
Selim Cinek9212de82017-02-06 16:04:28 -08001763 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001764 } else {
1765 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001766 if (mFinishScrollingCallback != null) {
1767 mFinishScrollingCallback.run();
1768 }
Selim Cinek67b22602014-03-10 15:40:16 +01001769 }
1770 }
1771
Selim Cinek9212de82017-02-06 16:04:28 -08001772 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1773 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001774
1775 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001776 final int top = -maxOverScrollY;
1777 final int bottom = maxOverScrollY + scrollRangeY;
1778
1779 boolean clampedY = false;
1780 if (newScrollY > bottom) {
1781 newScrollY = bottom;
1782 clampedY = true;
1783 } else if (newScrollY < top) {
1784 newScrollY = top;
1785 clampedY = true;
1786 }
1787
Selim Cinek9212de82017-02-06 16:04:28 -08001788 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001789
1790 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001791 }
1792
1793 /**
1794 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1795 * overscroll effect based on numPixels. By default this will also cancel animations on the
1796 * same overScroll edge.
1797 *
1798 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1799 * the rubber-banding logic.
1800 * @param onTop Should the effect be applied on top of the scroller.
1801 * @param animate Should an animation be performed.
1802 */
1803 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001804 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001805 }
1806
1807 /**
1808 * Set the effective overScroll amount which will be directly reflected in the layout.
1809 * By default this will also cancel animations on the same overScroll edge.
1810 *
1811 * @param amount The amount to overScroll by.
1812 * @param onTop Should the effect be applied on top of the scroller.
1813 * @param animate Should an animation be performed.
1814 */
1815 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1816 setOverScrollAmount(amount, onTop, animate, true);
1817 }
1818
1819 /**
1820 * Set the effective overScroll amount which will be directly reflected in the layout.
1821 *
1822 * @param amount The amount to overScroll by.
1823 * @param onTop Should the effect be applied on top of the scroller.
1824 * @param animate Should an animation be performed.
1825 * @param cancelAnimators Should running animations be cancelled.
1826 */
1827 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1828 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001829 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1830 }
1831
1832 /**
1833 * Set the effective overScroll amount which will be directly reflected in the layout.
1834 *
1835 * @param amount The amount to overScroll by.
1836 * @param onTop Should the effect be applied on top of the scroller.
1837 * @param animate Should an animation be performed.
1838 * @param cancelAnimators Should running animations be cancelled.
1839 * @param isRubberbanded The value which will be passed to
1840 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1841 */
1842 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1843 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001844 if (cancelAnimators) {
1845 mStateAnimator.cancelOverScrollAnimators(onTop);
1846 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001847 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001848 }
1849
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001850 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1851 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001852 amount = Math.max(0, amount);
1853 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001854 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001855 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001856 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001857 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001858 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001859 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001860 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001861 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001862 }
1863 }
1864
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001865 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001866 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1867 if (mDontReportNextOverScroll) {
1868 mDontReportNextOverScroll = false;
1869 return;
1870 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001871 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001872 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001873 }
1874 }
1875
1876 public void setOverscrollTopChangedListener(
1877 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1878 mOverscrollTopChangedListener = overscrollTopChangedListener;
1879 }
1880
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001881 public float getCurrentOverScrollAmount(boolean top) {
1882 return mAmbientState.getOverScrollAmount(top);
1883 }
1884
1885 public float getCurrentOverScrolledPixels(boolean top) {
1886 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
1887 }
1888
1889 private void setOverScrolledPixels(float amount, boolean onTop) {
1890 if (onTop) {
1891 mOverScrolledTopPixels = amount;
1892 } else {
1893 mOverScrolledBottomPixels = amount;
1894 }
1895 }
1896
Selim Cinek9212de82017-02-06 16:04:28 -08001897 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001898 // Treat animating scrolls differently; see #computeScroll() for why.
1899 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07001900 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001901 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001902 springBack();
1903 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02001904 float overScrollTop = getCurrentOverScrollAmount(true);
1905 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001906 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001907 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001908 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001909 }
Selim Cinek67b22602014-03-10 15:40:16 +01001910 }
Selim Cinek67b22602014-03-10 15:40:16 +01001911 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08001912 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001913 }
1914 }
1915
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001916 private void springBack() {
1917 int scrollRange = getScrollRange();
1918 boolean overScrolledTop = mOwnScrollY <= 0;
1919 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
1920 if (overScrolledTop || overScrolledBottom) {
1921 boolean onTop;
1922 float newAmount;
1923 if (overScrolledTop) {
1924 onTop = true;
1925 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07001926 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02001927 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001928 } else {
1929 onTop = false;
1930 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07001931 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001932 }
1933 setOverScrollAmount(newAmount, onTop, false);
1934 setOverScrollAmount(0.0f, onTop, true);
1935 mScroller.forceFinished(true);
1936 }
1937 }
1938
Selim Cinek67b22602014-03-10 15:40:16 +01001939 private int getScrollRange() {
Selim Cineka424c502016-04-05 13:07:54 -07001940 int contentHeight = getContentHeight();
Selim Cinekdb167372016-11-17 15:41:17 -08001941 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07001942 int imeInset = getImeInset();
1943 scrollRange += Math.min(imeInset, Math.max(0,
1944 getContentHeight() - (getHeight() - imeInset)));
1945 return scrollRange;
1946 }
1947
1948 private int getImeInset() {
1949 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01001950 }
1951
Selim Cinek343e6e22014-04-11 21:23:30 +02001952 /**
1953 * @return the first child which has visibility unequal to GONE
1954 */
Selim Cinekb55386d2015-12-16 17:26:49 -08001955 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02001956 int childCount = getChildCount();
1957 for (int i = 0; i < childCount; i++) {
1958 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001959 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08001960 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02001961 }
1962 }
1963 return null;
1964 }
1965
Selim Cinek4a1ac842014-05-01 15:51:58 +02001966 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001967 * @return the child before the given view which has visibility unequal to GONE
1968 */
1969 public ExpandableView getViewBeforeView(ExpandableView view) {
1970 ExpandableView previousView = null;
1971 int childCount = getChildCount();
1972 for (int i = 0; i < childCount; i++) {
1973 View child = getChildAt(i);
1974 if (child == view) {
1975 return previousView;
1976 }
1977 if (child.getVisibility() != View.GONE) {
1978 previousView = (ExpandableView) child;
1979 }
1980 }
1981 return null;
1982 }
1983
1984 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001985 * @return The first child which has visibility unequal to GONE which is currently below the
1986 * given translationY or equal to it.
1987 */
Selim Cinekef8c2252017-02-10 14:52:18 -08001988 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001989 int childCount = getChildCount();
1990 for (int i = 0; i < childCount; i++) {
1991 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08001992 if (child.getVisibility() == View.GONE) {
1993 continue;
1994 }
1995 float rowTranslation = child.getTranslationY();
1996 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001997 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08001998 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
1999 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2000 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2001 List<ExpandableNotificationRow> notificationChildren =
2002 row.getNotificationChildren();
2003 for (int childIndex = 0; childIndex < notificationChildren.size();
2004 childIndex++) {
2005 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2006 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2007 return rowChild;
2008 }
2009 }
2010 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002011 }
2012 }
2013 return null;
2014 }
2015
2016 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002017 * @return the last child which has visibility unequal to GONE
2018 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002019 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002020 int childCount = getChildCount();
2021 for (int i = childCount - 1; i >= 0; i--) {
2022 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002023 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002024 return child;
2025 }
2026 }
2027 return null;
2028 }
2029
Jorim Jaggi069cd032014-05-15 03:09:01 +02002030 /**
2031 * @return the number of children which have visibility unequal to GONE
2032 */
2033 public int getNotGoneChildCount() {
2034 int childCount = getChildCount();
2035 int count = 0;
2036 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002037 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002038 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002039 count++;
2040 }
2041 }
2042 return count;
2043 }
2044
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002045 public int getContentHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01002046 return mContentHeight;
2047 }
2048
2049 private void updateContentHeight() {
2050 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002051 float previousPaddingRequest = mPaddingBetweenElements;
2052 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002053 int numShownItems = 0;
2054 boolean finish = false;
Adrian Roos7a9551a2017-01-11 12:27:49 -08002055 int maxDisplayedNotifications = mAmbientState.isDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002056 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08002057 : mMaxDisplayedNotifications;
2058
Selim Cinek67b22602014-03-10 15:40:16 +01002059 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002060 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Selim Cinek281c2022016-10-13 19:14:43 -07002061 if (expandableView.getVisibility() != View.GONE
2062 && !expandableView.hasNoContentHeight()) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002063 boolean limitReached = maxDisplayedNotifications != -1
2064 && numShownItems >= maxDisplayedNotifications;
Selim Cinekbe2c4432017-05-30 12:11:09 -07002065 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isDark()
2066 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07002067 && expandableView instanceof ExpandableNotificationRow
2068 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
2069 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002070 expandableView = mShelf;
2071 finish = true;
2072 }
Selim Cinek42357e02016-02-24 18:48:01 -08002073 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002074 float padding;
2075 if (increasedPaddingAmount >= 0.0f) {
2076 padding = (int) NotificationUtils.interpolate(
2077 previousPaddingRequest,
2078 mIncreasedPaddingBetweenElements,
2079 increasedPaddingAmount);
2080 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002081 mPaddingBetweenElements,
2082 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002083 increasedPaddingAmount);
2084 } else {
2085 int ownPadding = (int) NotificationUtils.interpolate(
2086 0,
2087 mPaddingBetweenElements,
2088 1.0f + increasedPaddingAmount);
2089 if (previousPaddingAmount > 0.0f) {
2090 padding = (int) NotificationUtils.interpolate(
2091 ownPadding,
2092 mIncreasedPaddingBetweenElements,
2093 previousPaddingAmount);
2094 } else {
2095 padding = ownPadding;
2096 }
2097 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002098 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002099 if (height != 0) {
2100 height += padding;
2101 }
2102 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002103 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002104 numShownItems++;
2105 if (finish) {
2106 break;
2107 }
Selim Cinek67b22602014-03-10 15:40:16 +01002108 }
2109 }
Anthony Chen9fe1ee72017-04-07 13:53:37 -07002110 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002111 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002112 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002113 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002114 }
2115
Adrian Roos7d062c42017-03-30 15:11:43 -07002116 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002117 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002118 }
2119
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002120 @Override
Selim Cinekbe2c4432017-05-30 12:11:09 -07002121 public boolean hasPulsingNotifications() {
Adrian Roos7d062c42017-03-30 15:11:43 -07002122 return mPulsing != null;
2123 }
2124
Selim Cinekc22fff62016-05-20 12:44:30 -07002125 private void updateScrollability() {
2126 boolean scrollable = getScrollRange() > 0;
2127 if (scrollable != mScrollable) {
2128 mScrollable = scrollable;
2129 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002130 updateForwardAndBackwardScrollability();
2131 }
2132 }
2133
2134 private void updateForwardAndBackwardScrollability() {
2135 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2136 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2137 boolean changed = forwardScrollable != mForwardScrollable
2138 || backwardsScrollable != mBackwardScrollable;
2139 mForwardScrollable = forwardScrollable;
2140 mBackwardScrollable = backwardsScrollable;
2141 if (changed) {
2142 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002143 }
Selim Cinek67b22602014-03-10 15:40:16 +01002144 }
2145
Selim Cinek6811d722016-01-19 17:53:12 -08002146 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002147 // No need to update the background color if it's not being drawn.
2148 if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002149 return;
2150 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002151
Selim Cinek6811d722016-01-19 17:53:12 -08002152 updateBackgroundBounds();
2153 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002154 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2155 || areBoundsAnimating();
2156 if (!isExpanded()) {
2157 abortBackgroundAnimators();
2158 animate = false;
2159 }
2160 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002161 startBackgroundAnimation();
2162 } else {
2163 mCurrentBounds.set(mBackgroundBounds);
2164 applyCurrentBackgroundBounds();
2165 }
2166 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002167 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002168 }
Selim Cinek614576e2016-01-20 10:54:09 -08002169 mAnimateNextBackgroundBottom = false;
2170 mAnimateNextBackgroundTop = false;
2171 }
2172
Selim Cinek54680902016-10-19 16:49:44 -07002173 private void abortBackgroundAnimators() {
2174 if (mBottomAnimator != null) {
2175 mBottomAnimator.cancel();
2176 }
2177 if (mTopAnimator != null) {
2178 mTopAnimator.cancel();
2179 }
2180 }
2181
Selim Cinek614576e2016-01-20 10:54:09 -08002182 private boolean areBoundsAnimating() {
2183 return mBottomAnimator != null || mTopAnimator != null;
2184 }
2185
2186 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002187 // left and right are always instantly applied
2188 mCurrentBounds.left = mBackgroundBounds.left;
2189 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002190 startBottomAnimation();
2191 startTopAnimation();
2192 }
2193
2194 private void startTopAnimation() {
2195 int previousEndValue = mEndAnimationRect.top;
2196 int newEndValue = mBackgroundBounds.top;
2197 ObjectAnimator previousAnimator = mTopAnimator;
2198 if (previousAnimator != null && previousEndValue == newEndValue) {
2199 return;
2200 }
2201 if (!mAnimateNextBackgroundTop) {
2202 // just a local update was performed
2203 if (previousAnimator != null) {
2204 // we need to increase all animation keyframes of the previous animator by the
2205 // relative change to the end value
2206 int previousStartValue = mStartAnimationRect.top;
2207 PropertyValuesHolder[] values = previousAnimator.getValues();
2208 values[0].setIntValues(previousStartValue, newEndValue);
2209 mStartAnimationRect.top = previousStartValue;
2210 mEndAnimationRect.top = newEndValue;
2211 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2212 return;
2213 } else {
2214 // no new animation needed, let's just apply the value
2215 setBackgroundTop(newEndValue);
2216 return;
2217 }
2218 }
2219 if (previousAnimator != null) {
2220 previousAnimator.cancel();
2221 }
2222 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2223 mCurrentBounds.top, newEndValue);
2224 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2225 animator.setInterpolator(interpolator);
2226 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2227 // remove the tag when the animation is finished
2228 animator.addListener(new AnimatorListenerAdapter() {
2229 @Override
2230 public void onAnimationEnd(Animator animation) {
2231 mStartAnimationRect.top = -1;
2232 mEndAnimationRect.top = -1;
2233 mTopAnimator = null;
2234 }
2235 });
2236 animator.start();
2237 mStartAnimationRect.top = mCurrentBounds.top;
2238 mEndAnimationRect.top = newEndValue;
2239 mTopAnimator = animator;
2240 }
2241
2242 private void startBottomAnimation() {
2243 int previousStartValue = mStartAnimationRect.bottom;
2244 int previousEndValue = mEndAnimationRect.bottom;
2245 int newEndValue = mBackgroundBounds.bottom;
2246 ObjectAnimator previousAnimator = mBottomAnimator;
2247 if (previousAnimator != null && previousEndValue == newEndValue) {
2248 return;
2249 }
2250 if (!mAnimateNextBackgroundBottom) {
2251 // just a local update was performed
2252 if (previousAnimator != null) {
2253 // we need to increase all animation keyframes of the previous animator by the
2254 // relative change to the end value
2255 PropertyValuesHolder[] values = previousAnimator.getValues();
2256 values[0].setIntValues(previousStartValue, newEndValue);
2257 mStartAnimationRect.bottom = previousStartValue;
2258 mEndAnimationRect.bottom = newEndValue;
2259 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2260 return;
2261 } else {
2262 // no new animation needed, let's just apply the value
2263 setBackgroundBottom(newEndValue);
2264 return;
2265 }
2266 }
2267 if (previousAnimator != null) {
2268 previousAnimator.cancel();
2269 }
2270 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2271 mCurrentBounds.bottom, newEndValue);
2272 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2273 animator.setInterpolator(interpolator);
2274 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2275 // remove the tag when the animation is finished
2276 animator.addListener(new AnimatorListenerAdapter() {
2277 @Override
2278 public void onAnimationEnd(Animator animation) {
2279 mStartAnimationRect.bottom = -1;
2280 mEndAnimationRect.bottom = -1;
2281 mBottomAnimator = null;
2282 }
2283 });
2284 animator.start();
2285 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2286 mEndAnimationRect.bottom = newEndValue;
2287 mBottomAnimator = animator;
2288 }
2289
2290 private void setBackgroundTop(int top) {
2291 mCurrentBounds.top = top;
2292 applyCurrentBackgroundBounds();
2293 }
2294
2295 public void setBackgroundBottom(int bottom) {
2296 mCurrentBounds.bottom = bottom;
2297 applyCurrentBackgroundBounds();
2298 }
2299
2300 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002301 // If the background of the notification is not being drawn, then there is no need to
2302 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2303 if (!mShouldDrawNotificationBackground) {
2304 return;
2305 }
2306
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002307 mScrimController.setExcludedBackgroundArea(
Lucas Dupind285cf02018-01-18 09:18:23 -08002308 mFadingOut || mParentNotFullyVisible || mDarkAmount != 1 || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002309 : mCurrentBounds);
Selim Cinek614576e2016-01-20 10:54:09 -08002310 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002311 }
2312
2313 /**
2314 * Update the background bounds to the new desired bounds
2315 */
2316 private void updateBackgroundBounds() {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01002317 if (mAmbientState.isPanelFullWidth()) {
2318 mBackgroundBounds.left = 0;
2319 mBackgroundBounds.right = getWidth();
2320 } else {
2321 getLocationInWindow(mTempInt2);
2322 mBackgroundBounds.left = mTempInt2[0];
2323 mBackgroundBounds.right = mTempInt2[0] + getWidth();
2324 }
Selim Cinekb95fd182017-12-21 13:03:32 -08002325 mBackgroundBounds.left += mSidePaddings;
2326 mBackgroundBounds.right -= mSidePaddings;
Selim Cinek614576e2016-01-20 10:54:09 -08002327 if (!mIsExpanded) {
2328 mBackgroundBounds.top = 0;
2329 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002330 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002331 }
2332 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002333 int top = 0;
2334 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002335 // Round Y up to avoid seeing the background during animation
2336 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002337 if (mAnimateNextBackgroundTop
2338 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2339 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002340 // we're ending up at the same location as we are now, lets just skip the animation
2341 top = finalTranslationY;
2342 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002343 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002344 }
2345 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002346 ActivatableNotificationView lastView =
2347 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2348 ? mShelf
2349 : mLastVisibleBackgroundChild;
2350 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002351 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002352 int finalTranslationY;
2353 if (lastView == mShelf) {
2354 finalTranslationY = (int) mShelf.getTranslationY();
2355 } else {
2356 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2357 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002358 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002359 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek6811d722016-01-19 17:53:12 -08002360 finalBottom = Math.min(finalBottom, getHeight());
Selim Cinek614576e2016-01-20 10:54:09 -08002361 if (mAnimateNextBackgroundBottom
2362 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2363 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002364 // we're ending up at the same location as we are now, lets just skip the animation
2365 bottom = finalBottom;
2366 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002367 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2368 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002369 bottom = Math.min(bottom, getHeight());
2370 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002371 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002372 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002373 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002374 }
Selim Cinek355652a2016-12-07 13:32:12 -08002375 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002376 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002377 } else {
2378 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002379 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002380 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002381 mBackgroundBounds.top = top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002382 mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
Selim Cinek6811d722016-01-19 17:53:12 -08002383 }
2384
Selim Cinek614576e2016-01-20 10:54:09 -08002385 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2386 int childCount = getChildCount();
2387 for (int i = 0; i < childCount; i++) {
2388 View child = getChildAt(i);
2389 if (child.getVisibility() != View.GONE
2390 && child instanceof ExpandableNotificationRow) {
2391 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2392 if (row.isPinned()) {
2393 return row;
2394 }
2395 }
2396 }
2397 return null;
2398 }
2399
2400 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002401 int childCount = getChildCount();
2402 for (int i = childCount - 1; i >= 0; i--) {
2403 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002404 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2405 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002406 return (ActivatableNotificationView) child;
2407 }
2408 }
2409 return null;
2410 }
2411
Selim Cinek614576e2016-01-20 10:54:09 -08002412 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002413 int childCount = getChildCount();
2414 for (int i = 0; i < childCount; i++) {
2415 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002416 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2417 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002418 return (ActivatableNotificationView) child;
2419 }
2420 }
2421 return null;
2422 }
2423
Selim Cinek67b22602014-03-10 15:40:16 +01002424 /**
2425 * Fling the scroll view
2426 *
2427 * @param velocityY The initial velocity in the Y direction. Positive
2428 * numbers mean that the finger/cursor is moving down the screen,
2429 * which means we want to scroll towards the top.
2430 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002431 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002432 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002433 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002434
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002435 float topAmount = getCurrentOverScrollAmount(true);
2436 float bottomAmount = getCurrentOverScrollAmount(false);
2437 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002438 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002439 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002440 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002441 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002442 * mOverflingDistance + topAmount;
2443 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002444 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002445 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002446 mMaxOverScroll = Math.abs(velocityY) / 1000f
2447 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2448 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002449 } else {
2450 // it will be set once we reach the boundary
2451 mMaxOverScroll = 0.0f;
2452 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002453 int minScrollY = Math.max(0, scrollRange);
2454 if (mExpandedInThisMotion) {
2455 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2456 }
Selim Cinek9212de82017-02-06 16:04:28 -08002457 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2458 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002459
Selim Cinek9212de82017-02-06 16:04:28 -08002460 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002461 }
2462 }
2463
Selim Cinek1408eb52014-06-02 14:45:38 +02002464 /**
2465 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2466 * overScroll view (i.e QS).
2467 */
2468 private boolean shouldOverScrollFling(int initialVelocity) {
2469 float topOverScroll = getCurrentOverScrollAmount(true);
2470 return mScrolledToTopOnFirstDown
2471 && !mExpandedInThisMotion
2472 && topOverScroll > mMinTopOverScrollToEscape
2473 && initialVelocity > 0;
2474 }
2475
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002476 /**
2477 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2478 * account.
2479 *
2480 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002481 * @param animate whether to animate the change
2482 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2483 * {@code qsHeight} is the final top padding
2484 */
Jason Monk16ac3772016-02-10 15:39:21 -05002485 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002486 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002487 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002488 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002489 if (topPadding + minStackHeight > getHeight()) {
2490 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002491 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002492 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002493 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002494 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002495 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002496 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002497 }
2498
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002499 public int getLayoutMinHeight() {
Anthony Chen9e05d462017-04-07 10:10:21 -07002500 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002501 }
2502
Selim Cineka981d082016-08-03 14:45:31 -07002503 public int getFirstChildIntrinsicHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002504 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002505 int firstChildMinHeight = firstChild != null
2506 ? firstChild.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002507 : mEmptyShadeView != null
Selim Cineka981d082016-08-03 14:45:31 -07002508 ? mEmptyShadeView.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002509 : mCollapsedSize;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002510 if (mOwnScrollY > 0) {
2511 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2512 }
Selim Cinek94c2d822016-07-13 18:50:04 -07002513 return firstChildMinHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002514 }
2515
2516 public float getTopPaddingOverflow() {
2517 return mTopPaddingOverflow;
2518 }
2519
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002520 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002521 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002522 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002523 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002524 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002525 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002526 shelfHeight = mShelf.getIntrinsicHeight();
2527 }
2528 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002529 }
2530
Selim Cinek1408eb52014-06-02 14:45:38 +02002531 private int clampPadding(int desiredPadding) {
2532 return Math.max(desiredPadding, mIntrinsicPadding);
2533 }
2534
Selim Cinekfed1ab62014-06-17 14:10:33 -07002535 private float getRubberBandFactor(boolean onTop) {
2536 if (!onTop) {
2537 return RUBBER_BAND_FACTOR_NORMAL;
2538 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002539 if (mExpandedInThisMotion) {
2540 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002541 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002542 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2543 } else if (mScrolledToTopOnFirstDown) {
2544 return 1.0f;
2545 }
2546 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002547 }
2548
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002549 /**
2550 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2551 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2552 * overscroll view (e.g. expand QS).
2553 */
2554 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002555 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002556 || !mScrolledToTopOnFirstDown;
2557 }
2558
Selim Cinek67b22602014-03-10 15:40:16 +01002559 private void endDrag() {
2560 setIsBeingDragged(false);
2561
2562 recycleVelocityTracker();
2563
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002564 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2565 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2566 }
2567 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2568 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2569 }
Selim Cinek67b22602014-03-10 15:40:16 +01002570 }
2571
Jorim Jaggi56306252014-07-03 00:40:09 +02002572 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2573 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2574 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2575 }
2576
Selim Cinek67b22602014-03-10 15:40:16 +01002577 @Override
2578 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002579 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002580 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002581 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002582 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002583 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2584 }
Selim Cinek67b22602014-03-10 15:40:16 +01002585 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002586 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002587 scrollWantsIt = onInterceptTouchEventScroll(ev);
2588 }
2589 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002590 if (!mIsBeingDragged
2591 && !mExpandingNotification
2592 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002593 && !mOnlyScrollingInThisMotion
2594 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002595 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2596 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002597 // Check if we need to clear any snooze leavebehinds
2598 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002599 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07002600 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2601 && !scrollWantsIt) {
2602 mCheckForLeavebehind = false;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002603 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
2604 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2605 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07002606 }
2607 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2608 mCheckForLeavebehind = true;
2609 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002610 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2611 }
2612
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002613 private void handleEmptySpaceClick(MotionEvent ev) {
2614 switch (ev.getActionMasked()) {
2615 case MotionEvent.ACTION_MOVE:
2616 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2617 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2618 mTouchIsClick = false;
2619 }
2620 break;
2621 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002622 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002623 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2624 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2625 }
2626 break;
2627 }
2628 }
2629
Selim Cinek1408eb52014-06-02 14:45:38 +02002630 private void initDownStates(MotionEvent ev) {
2631 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2632 mExpandedInThisMotion = false;
2633 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002634 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002635 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002636 mTouchIsClick = true;
2637 mInitialTouchX = ev.getX();
2638 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002639 }
Selim Cinek67b22602014-03-10 15:40:16 +01002640 }
2641
Selim Cinekef5127e2015-12-21 16:55:58 -08002642 public void setChildTransferInProgress(boolean childTransferInProgress) {
2643 mChildTransferInProgress = childTransferInProgress;
2644 }
2645
Christoph Studer068f5922014-04-08 17:43:07 -04002646 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002647 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002648 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002649 // we only call our internal methods if this is actually a removal and not just a
2650 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002651 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002652 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002653 }
2654 }
2655
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002656 @Override
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002657 public void cleanUpViewState(View child) {
2658 if (child == mTranslatingParentView) {
2659 mTranslatingParentView = null;
2660 }
2661 mCurrentStackScrollState.removeViewStateForView(child);
2662 }
2663
Selim Cinek197823d2016-03-24 13:06:00 -07002664 @Override
2665 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2666 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2667 if (disallowIntercept) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002668 cancelLongPress();
Selim Cinek197823d2016-03-24 13:06:00 -07002669 }
2670 }
2671
Selim Cineka5703182016-05-11 21:23:16 -04002672 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002673 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002674 // This is only a position change, don't do anything special
2675 return;
2676 }
Selim Cinekd1395642016-04-28 12:22:42 -07002677 ExpandableView expandableView = (ExpandableView) child;
2678 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002679 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002680 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002681 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002682 if (animationGenerated) {
2683 if (!mSwipedOutViews.contains(child)) {
Selim Cineka5703182016-05-11 21:23:16 -04002684 container.getOverlay().add(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002685 } else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002686 container.addTransientView(child, 0);
2687 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002688 }
2689 } else {
2690 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002691 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002692 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002693
Selim Cineke9bad242016-06-15 11:46:37 -07002694 focusNextViewIfFocused(child);
2695 }
2696
2697 private void focusNextViewIfFocused(View view) {
2698 if (view instanceof ExpandableNotificationRow) {
2699 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2700 if (row.shouldRefocusOnDismiss()) {
2701 View nextView = row.getChildAfterViewWhenDismissed();
2702 if (nextView == null) {
2703 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2704 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2705 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002706 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002707 }
2708 if (nextView != null) {
2709 nextView.requestAccessibilityFocus();
2710 }
2711 }
2712 }
2713
Selim Cinekc27437b2014-05-14 10:23:33 +02002714 }
2715
Selim Cinekb5605e52015-02-20 18:21:41 +01002716 private boolean isChildInGroup(View child) {
2717 return child instanceof ExpandableNotificationRow
2718 && mGroupManager.isChildInGroupWithSummary(
2719 ((ExpandableNotificationRow) child).getStatusBarNotification());
2720 }
2721
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002722 /**
2723 * Generate a remove animation for a child view.
2724 *
2725 * @param child The view to generate the remove animation for.
2726 * @return Whether an animation was generated.
2727 */
2728 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002729 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002730 mAddedHeadsUpChildren.remove(child);
2731 return false;
2732 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002733 if (isClickedHeadsUp(child)) {
2734 // An animation is already running, add it to the Overlay
2735 mClearOverlayViewsWhenFinished.add(child);
2736 return true;
2737 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002738 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002739 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002740 // Generate Animations
2741 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002742 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002743 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002744 } else {
2745 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002746 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002747 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002748 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002749 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002750 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002751 }
2752
Selim Cinek0fccc722015-07-29 17:04:36 -07002753 private boolean isClickedHeadsUp(View child) {
2754 return HeadsUpManager.isClickedHeadsUpNotification(child);
2755 }
2756
Selim Cineke0890e52015-06-17 11:17:08 -07002757 /**
2758 * Remove a removed child view from the heads up animations if it was just added there
2759 *
2760 * @return whether any child was removed from the list to animate
2761 */
2762 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2763 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002764 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2765 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002766 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002767 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002768 mTmpList.add(eventPair);
2769 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002770 }
2771 }
Selim Cineke0890e52015-06-17 11:17:08 -07002772 if (hasAddEvent) {
2773 // This child was just added lets remove all events.
2774 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002775 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002776 }
2777 mTmpList.clear();
2778 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002779 }
2780
Selim Cinek572bbd42014-04-25 16:43:27 +02002781 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002782 * @param child the child to query
2783 * @return whether a view is not a top level child but a child notification and that group is
2784 * not expanded
2785 */
2786 private boolean isChildInInvisibleGroup(View child) {
2787 if (child instanceof ExpandableNotificationRow) {
2788 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2789 ExpandableNotificationRow groupSummary =
2790 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2791 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002792 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002793 }
2794 }
2795 return false;
2796 }
2797
2798 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002799 * Updates the scroll position when a child was removed
2800 *
2801 * @param removedChild the removed child
2802 */
Selim Cinek61633a82016-01-25 15:54:10 -08002803 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002804 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002805 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2806 int padding;
2807 if (increasedPaddingAmount >= 0) {
2808 padding = (int) NotificationUtils.interpolate(
2809 mPaddingBetweenElements,
2810 mIncreasedPaddingBetweenElements,
2811 increasedPaddingAmount);
2812 } else {
2813 padding = (int) NotificationUtils.interpolate(
2814 0,
2815 mPaddingBetweenElements,
2816 1.0f + increasedPaddingAmount);
2817 }
Selim Cinek61633a82016-01-25 15:54:10 -08002818 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002819 int endPosition = startingPosition + childHeight;
2820 if (endPosition <= mOwnScrollY) {
2821 // This child is fully scrolled of the top, so we have to deduct its height from the
2822 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002823 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002824 } else if (startingPosition < mOwnScrollY) {
2825 // This child is currently being scrolled into, set the scroll position to the start of
2826 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002827 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002828 }
2829 }
2830
Selim Cinekd7c4e002014-07-04 18:36:42 +02002831 private int getIntrinsicHeight(View view) {
2832 if (view instanceof ExpandableView) {
2833 ExpandableView expandableView = (ExpandableView) view;
2834 return expandableView.getIntrinsicHeight();
2835 }
2836 return view.getHeight();
2837 }
2838
Selim Cinek1f624952017-06-08 19:11:50 -07002839 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002840 ExpandableNotificationRow childInGroup = null;
2841 ExpandableNotificationRow requestedRow = null;
2842 if (isChildInGroup(requestedView)) {
2843 // We're asking for a child in a group. Calculate the position of the parent first,
2844 // then within the parent.
2845 childInGroup = (ExpandableNotificationRow) requestedView;
2846 requestedView = requestedRow = childInGroup.getNotificationParent();
2847 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002848 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002849 float previousPaddingRequest = mPaddingBetweenElements;
2850 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002851 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002852 ExpandableView child = (ExpandableView) getChildAt(i);
2853 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002854 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002855 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002856 float padding;
2857 if (increasedPaddingAmount >= 0.0f) {
2858 padding = (int) NotificationUtils.interpolate(
2859 previousPaddingRequest,
2860 mIncreasedPaddingBetweenElements,
2861 increasedPaddingAmount);
2862 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002863 mPaddingBetweenElements,
2864 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002865 increasedPaddingAmount);
2866 } else {
2867 int ownPadding = (int) NotificationUtils.interpolate(
2868 0,
2869 mPaddingBetweenElements,
2870 1.0f + increasedPaddingAmount);
2871 if (previousPaddingAmount > 0.0f) {
2872 padding = (int) NotificationUtils.interpolate(
2873 ownPadding,
2874 mIncreasedPaddingBetweenElements,
2875 previousPaddingAmount);
2876 } else {
2877 padding = ownPadding;
2878 }
2879 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08002880 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002881 if (position != 0) {
2882 position += padding;
2883 }
2884 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002885 }
Adrian Roos4a579672016-05-24 16:54:37 -07002886 if (child == requestedView) {
2887 if (requestedRow != null) {
2888 position += requestedRow.getPositionOfChild(childInGroup);
2889 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002890 return position;
2891 }
Selim Cinek61633a82016-01-25 15:54:10 -08002892 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02002893 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002894 }
2895 }
2896 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02002897 }
2898
2899 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002900 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02002901 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002902 onViewAddedInternal(child);
2903 }
2904
Selim Cinek614576e2016-01-20 10:54:09 -08002905 private void updateFirstAndLastBackgroundViews() {
2906 ActivatableNotificationView firstChild = getFirstChildWithBackground();
2907 ActivatableNotificationView lastChild = getLastChildWithBackground();
Selim Cinek0fe07392017-11-09 13:26:34 -08002908 boolean firstChanged = firstChild != mFirstVisibleBackgroundChild;
2909 boolean lastChanged = lastChild != mLastVisibleBackgroundChild;
Selim Cinek614576e2016-01-20 10:54:09 -08002910 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek0fe07392017-11-09 13:26:34 -08002911 mAnimateNextBackgroundTop = firstChanged;
2912 mAnimateNextBackgroundBottom = lastChanged;
Selim Cinek614576e2016-01-20 10:54:09 -08002913 } else {
2914 mAnimateNextBackgroundTop = false;
2915 mAnimateNextBackgroundBottom = false;
2916 }
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002917 if (firstChanged && mFirstVisibleBackgroundChild != null
2918 && !mFirstVisibleBackgroundChild.isRemoved()) {
2919 mFirstVisibleBackgroundChild.setTopRoundness(0.0f,
2920 mFirstVisibleBackgroundChild.isShown());
Selim Cinek0fe07392017-11-09 13:26:34 -08002921 }
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002922 if (lastChanged && mLastVisibleBackgroundChild != null
2923 && !mLastVisibleBackgroundChild.isRemoved()) {
Selim Cinek2871bef2017-11-22 08:40:00 -08002924 mLastVisibleBackgroundChild.setBottomRoundness(0.0f,
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002925 mLastVisibleBackgroundChild.isShown());
Selim Cinek0fe07392017-11-09 13:26:34 -08002926 }
Selim Cinek614576e2016-01-20 10:54:09 -08002927 mFirstVisibleBackgroundChild = firstChild;
2928 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08002929 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek515b2032017-11-15 10:20:19 -08002930 applyRoundedNess();
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002931 }
2932
2933 private void applyRoundedNess() {
Selim Cinek0fe07392017-11-09 13:26:34 -08002934 if (mFirstVisibleBackgroundChild != null) {
Selim Cinekb95fd182017-12-21 13:03:32 -08002935 mFirstVisibleBackgroundChild.setTopRoundness(1.0f,
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002936 mFirstVisibleBackgroundChild.isShown()
2937 && !mChildrenToAddAnimated.contains(mFirstVisibleBackgroundChild));
Selim Cinek0fe07392017-11-09 13:26:34 -08002938 }
2939 if (mLastVisibleBackgroundChild != null) {
Selim Cinek2871bef2017-11-22 08:40:00 -08002940 mLastVisibleBackgroundChild.setBottomRoundness(1.0f,
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002941 mLastVisibleBackgroundChild.isShown()
2942 && !mChildrenToAddAnimated.contains(mLastVisibleBackgroundChild));
Selim Cinek0fe07392017-11-09 13:26:34 -08002943 }
Selim Cinek515b2032017-11-15 10:20:19 -08002944 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002945 }
2946
Selim Cinekb5605e52015-02-20 18:21:41 +01002947 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002948 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02002949 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00002950 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002951 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02002952 updateChronometerForChild(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002953 }
2954
Selim Cinekd06c41c2015-07-06 14:51:36 -07002955 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002956 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002957 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002958 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07002959 }
2960 }
2961
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002962 @Override
Selim Cinekd1395642016-04-28 12:22:42 -07002963 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
2964 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01002965 }
2966
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002967 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01002968 public void notifyGroupChildAdded(View row) {
2969 onViewAddedInternal(row);
2970 }
2971
Jorim Jaggi75c95042014-05-16 19:09:59 +02002972 public void setAnimationsEnabled(boolean animationsEnabled) {
2973 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02002974 updateNotificationAnimationStates();
2975 }
2976
2977 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002978 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08002979 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02002980 int childCount = getChildCount();
2981 for (int i = 0; i < childCount; i++) {
2982 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07002983 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02002984 updateAnimationState(running, child);
2985 }
2986 }
2987
Selim Cinek51ae05d2014-09-09 15:51:38 +02002988 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002989 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08002990 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002991 }
2992
Selim Cinek2627d722018-01-19 12:16:49 -08002993 @Override
2994 public void setExpandingNotification(ExpandableNotificationRow row) {
2995 mAmbientState.setExpandingNotification(row);
2996 requestChildrenUpdate();
2997 }
2998
2999 @Override
3000 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3001 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3002 requestChildrenUpdate();
3003 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003004
Selim Cinekcab4a602014-09-03 14:47:57 +02003005 private void updateAnimationState(boolean running, View child) {
3006 if (child instanceof ExpandableNotificationRow) {
3007 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3008 row.setIconAnimationRunning(running);
3009 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003010 }
3011
3012 public boolean isAddOrRemoveAnimationPending() {
3013 return mNeedsAnimation
3014 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3015 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003016
3017 @Override
Jorim Jaggif6411742014-08-05 17:10:43 +00003018 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003019 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003020 // Generate Animations
3021 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003022 if (fromMoreCard) {
3023 mFromMoreCardAdditions.add(child);
3024 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003025 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003026 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003027 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003028 mAddedHeadsUpChildren.add(child);
3029 mChildrenToAddAnimated.remove(child);
3030 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003031 }
3032
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003033 @Override
Selim Cinek572bbd42014-04-25 16:43:27 +02003034 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003035 int currentIndex = indexOfChild(child);
3036 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003037 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08003038 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003039 removeView(child);
3040 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08003041 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003042 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003043 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003044 mChildrenChangingPositions.add(child);
3045 mNeedsAnimation = true;
3046 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003047 }
3048 }
3049
Selim Cinekf4c19962014-05-01 21:55:31 +02003050 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003051 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003052 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003053 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003054 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003055 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003056 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003057 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
3058 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003059 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003060 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003061 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003062 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003063 } else {
3064 applyCurrentState();
3065 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003066 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003067 }
3068
3069 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003070 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003071 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003072 generateChildAdditionEvents();
3073 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02003074 generateSnapBackEvents();
3075 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003076 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003077 generateActivateEvent();
3078 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003079 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003080 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003081 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003082 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003083 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003084 generateAnimateEverythingEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003085 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003086 }
3087
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003088 private void generateHeadsUpAnimationEvents() {
3089 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003090 ExpandableNotificationRow row = eventPair.first;
3091 boolean isHeadsUp = eventPair.second;
3092 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3093 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003094 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003095 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003096 type = row.wasJustClicked()
3097 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3098 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003099 if (row.isChildInGroup()) {
3100 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003101 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003102 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003103 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08003104 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04003105 if (viewState == null) {
3106 // A view state was never generated for this view, so we don't need to animate
3107 // this. This may happen with notification children.
3108 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003109 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003110 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3111 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3112 // Our custom add animation
3113 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3114 } else {
3115 // Normal add animation
3116 type = AnimationEvent.ANIMATION_TYPE_ADD;
3117 }
3118 onBottom = !pinnedAndClosed;
3119 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003120 }
3121 AnimationEvent event = new AnimationEvent(row, type);
3122 event.headsUpFromBottom = onBottom;
3123 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003124 }
3125 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003126 mAddedHeadsUpChildren.clear();
3127 }
3128
Selim Cinekbbcebde2016-11-09 18:28:20 -08003129 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003130 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3131 return false;
3132 }
3133 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003134 }
3135
Selim Cinekb5605e52015-02-20 18:21:41 +01003136 private void generateGroupExpansionEvent() {
3137 // Generate a group expansion/collapsing event if there is such a group at all
3138 if (mExpandedGroupView != null) {
3139 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3140 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3141 mExpandedGroupView = null;
3142 }
3143 }
3144
Selim Cineka5e211b2014-08-11 17:35:48 +02003145 private void generateViewResizeEvent() {
3146 if (mNeedViewResizeAnimation) {
3147 mAnimationEvents.add(
3148 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3149 }
3150 mNeedViewResizeAnimation = false;
3151 }
3152
Selim Cinekeb973562014-05-02 17:07:49 +02003153 private void generateSnapBackEvents() {
3154 for (View child : mSnappedBackChildren) {
3155 mAnimationEvents.add(new AnimationEvent(child,
3156 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3157 }
3158 mSnappedBackChildren.clear();
3159 }
3160
3161 private void generateDragEvents() {
3162 for (View child : mDragAnimPendingChildren) {
3163 mAnimationEvents.add(new AnimationEvent(child,
3164 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3165 }
3166 mDragAnimPendingChildren.clear();
3167 }
3168
Selim Cinek572bbd42014-04-25 16:43:27 +02003169 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003170 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003171 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003172
3173 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003174 float removedTranslation = child.getTranslationY();
3175 boolean ignoreChildren = true;
3176 if (child instanceof ExpandableNotificationRow) {
3177 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3178 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3179 removedTranslation = row.getTranslationWhenRemoved();
3180 ignoreChildren = false;
3181 }
Selim Cinek51052042017-07-04 12:07:55 +02003182 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003183 }
Selim Cinek51052042017-07-04 12:07:55 +02003184 if (!childWasSwipedOut) {
3185 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003186 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Selim Cinek51052042017-07-04 12:07:55 +02003187 }
3188 int animationType = childWasSwipedOut
3189 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3190 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3191 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003192 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3193 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003194 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003195 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003196 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003197 mChildrenToRemoveAnimated.clear();
3198 }
3199
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003200 private void generatePositionChangeEvents() {
3201 for (View child : mChildrenChangingPositions) {
3202 mAnimationEvents.add(new AnimationEvent(child,
3203 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3204 }
3205 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003206 if (mGenerateChildOrderChangedEvent) {
3207 mAnimationEvents.add(new AnimationEvent(null,
3208 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3209 mGenerateChildOrderChangedEvent = false;
3210 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003211 }
3212
Selim Cinek572bbd42014-04-25 16:43:27 +02003213 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003214 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003215 if (mFromMoreCardAdditions.contains(child)) {
3216 mAnimationEvents.add(new AnimationEvent(child,
3217 AnimationEvent.ANIMATION_TYPE_ADD,
3218 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3219 } else {
3220 mAnimationEvents.add(new AnimationEvent(child,
3221 AnimationEvent.ANIMATION_TYPE_ADD));
3222 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003223 }
3224 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003225 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003226 }
3227
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003228 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003229 if (mTopPaddingNeedsAnimation) {
3230 mAnimationEvents.add(
3231 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
3232 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003233 mTopPaddingNeedsAnimation = false;
3234 }
3235
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003236 private void generateActivateEvent() {
3237 if (mActivateNeedsAnimation) {
3238 mAnimationEvents.add(
3239 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3240 }
3241 mActivateNeedsAnimation = false;
3242 }
3243
Selim Cinekd9acca52014-09-01 22:33:25 +02003244 private void generateAnimateEverythingEvent() {
3245 if (mEverythingNeedsAnimation) {
3246 mAnimationEvents.add(
3247 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3248 }
3249 mEverythingNeedsAnimation = false;
3250 }
3251
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003252 private void generateDimmedEvent() {
3253 if (mDimmedNeedsAnimation) {
3254 mAnimationEvents.add(
3255 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3256 }
3257 mDimmedNeedsAnimation = false;
3258 }
3259
Jorim Jaggiae441282014-08-01 02:45:18 +02003260 private void generateHideSensitiveEvent() {
3261 if (mHideSensitiveNeedsAnimation) {
3262 mAnimationEvents.add(
3263 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3264 }
3265 mHideSensitiveNeedsAnimation = false;
3266 }
3267
John Spurlockbf370992014-06-17 13:58:31 -04003268 private void generateDarkEvent() {
3269 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003270 AnimationEvent ev = new AnimationEvent(null,
3271 AnimationEvent.ANIMATION_TYPE_DARK,
3272 new AnimationFilter()
3273 .animateDark()
3274 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003275 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3276 mAnimationEvents.add(ev);
Selim Cinek972123d2016-05-03 14:25:58 -07003277 startBackgroundFadeIn();
John Spurlockbf370992014-06-17 13:58:31 -04003278 }
3279 mDarkNeedsAnimation = false;
3280 }
3281
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003282 private void generateGoToFullShadeEvent() {
3283 if (mGoToFullShadeNeedsAnimation) {
3284 mAnimationEvents.add(
3285 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3286 }
3287 mGoToFullShadeNeedsAnimation = false;
3288 }
3289
Selim Cinek67b22602014-03-10 15:40:16 +01003290 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003291 if (!isScrollingEnabled()) {
3292 return false;
3293 }
Selim Cinek67b22602014-03-10 15:40:16 +01003294 /*
3295 * This method JUST determines whether we want to intercept the motion.
3296 * If we return true, onMotionEvent will be called and we do the actual
3297 * scrolling there.
3298 */
3299
3300 /*
3301 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003302 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003303 * motion.
3304 */
3305 final int action = ev.getAction();
3306 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3307 return true;
3308 }
3309
Selim Cinek67b22602014-03-10 15:40:16 +01003310 switch (action & MotionEvent.ACTION_MASK) {
3311 case MotionEvent.ACTION_MOVE: {
3312 /*
3313 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003314 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003315 */
3316
3317 /*
3318 * Locally do absolute value. mLastMotionY is set to the y value
3319 * of the down event.
3320 */
3321 final int activePointerId = mActivePointerId;
3322 if (activePointerId == INVALID_POINTER) {
3323 // If we don't have a valid id, the touch down wasn't on content.
3324 break;
3325 }
3326
3327 final int pointerIndex = ev.findPointerIndex(activePointerId);
3328 if (pointerIndex == -1) {
3329 Log.e(TAG, "Invalid pointerId=" + activePointerId
3330 + " in onInterceptTouchEvent");
3331 break;
3332 }
3333
3334 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003335 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003336 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003337 final int xDiff = Math.abs(x - mDownX);
3338 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003339 setIsBeingDragged(true);
3340 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003341 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003342 initVelocityTrackerIfNotExists();
3343 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003344 }
3345 break;
3346 }
3347
3348 case MotionEvent.ACTION_DOWN: {
3349 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303350 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07003351 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01003352 setIsBeingDragged(false);
3353 recycleVelocityTracker();
3354 break;
3355 }
3356
3357 /*
3358 * Remember location of down touch.
3359 * ACTION_DOWN always refers to pointer index 0.
3360 */
3361 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003362 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003363 mActivePointerId = ev.getPointerId(0);
3364
3365 initOrResetVelocityTracker();
3366 mVelocityTracker.addMovement(ev);
3367 /*
3368 * If being flinged and user touches the screen, initiate drag;
3369 * otherwise don't. mScroller.isFinished should be false when
3370 * being flinged.
3371 */
3372 boolean isBeingDragged = !mScroller.isFinished();
3373 setIsBeingDragged(isBeingDragged);
3374 break;
3375 }
3376
3377 case MotionEvent.ACTION_CANCEL:
3378 case MotionEvent.ACTION_UP:
3379 /* Release the drag */
3380 setIsBeingDragged(false);
3381 mActivePointerId = INVALID_POINTER;
3382 recycleVelocityTracker();
3383 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08003384 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01003385 }
3386 break;
3387 case MotionEvent.ACTION_POINTER_UP:
3388 onSecondaryPointerUp(ev);
3389 break;
3390 }
3391
3392 /*
3393 * The only time we want to intercept motion events is if we are in the
3394 * drag mode.
3395 */
3396 return mIsBeingDragged;
3397 }
3398
Muyuan Li333a4fc2016-04-16 17:13:46 -07003399 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3400 return new StackScrollAlgorithm(context);
3401 }
3402
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003403 /**
3404 * @return Whether the specified motion event is actually happening over the content.
3405 */
3406 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003407 return isInContentBounds(event.getY());
3408 }
3409
3410 /**
3411 * @return Whether a y coordinate is inside the content.
3412 */
3413 public boolean isInContentBounds(float y) {
3414 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003415 }
3416
Selim Cinek67b22602014-03-10 15:40:16 +01003417 private void setIsBeingDragged(boolean isDragged) {
3418 mIsBeingDragged = isDragged;
3419 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003420 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003421 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003422 }
3423 }
3424
3425 @Override
3426 public void onWindowFocusChanged(boolean hasWindowFocus) {
3427 super.onWindowFocusChanged(hasWindowFocus);
3428 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003429 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003430 }
3431 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003432
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003433 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003434 public void clearChildFocus(View child) {
3435 super.clearChildFocus(child);
3436 if (mForcedScroll == child) {
3437 mForcedScroll = null;
3438 }
3439 }
3440
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003441 public void requestDisallowLongPress() {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003442 cancelLongPress();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003443 }
3444
Adrian Roosfa139752016-04-27 09:59:08 -07003445 public void requestDisallowDismiss() {
3446 mDisallowDismissInThisMotion = true;
3447 }
3448
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003449 public void cancelLongPress() {
3450 mSwipeHelper.cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02003451 }
3452
Selim Cinekfab078b2014-03-27 22:45:58 +01003453 @Override
3454 public boolean isScrolledToTop() {
3455 return mOwnScrollY == 0;
3456 }
3457
3458 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003459 public boolean isScrolledToBottom() {
3460 return mOwnScrollY >= getScrollRange();
3461 }
3462
3463 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003464 public View getHostView() {
3465 return this;
3466 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003467
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003468 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003469 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003470 }
3471
Mady Mellorc2dbe492017-03-30 13:22:03 -07003472 public void checkSnoozeLeavebehind() {
3473 if (mCheckForLeavebehind) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09003474 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
3475 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3476 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003477 mCheckForLeavebehind = false;
3478 }
3479 }
3480
3481 public void resetCheckSnoozeLeavebehind() {
3482 mCheckForLeavebehind = true;
3483 }
3484
Selim Cinek1685e632014-04-08 02:27:49 +02003485 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003486 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003487 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003488 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02003489 }
3490
3491 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003492 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07003493 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08003494 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003495 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003496 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003497 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003498 clearTemporaryViews();
3499 clearUserLockedViews();
3500 }
3501 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01003502
Selim Cinek5b1591a2017-07-03 17:05:01 +02003503 private void clearUserLockedViews() {
3504 for (int i = 0; i < getChildCount(); i++) {
3505 ExpandableView child = (ExpandableView) getChildAt(i);
3506 if (child instanceof ExpandableNotificationRow) {
3507 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3508 row.setUserLocked(false);
3509 }
3510 }
3511 }
3512
3513 private void clearTemporaryViews() {
3514 // lets make sure nothing is in the overlay / transient anymore
3515 clearTemporaryViews(this);
3516 for (int i = 0; i < getChildCount(); i++) {
3517 ExpandableView child = (ExpandableView) getChildAt(i);
3518 if (child instanceof ExpandableNotificationRow) {
3519 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3520 clearTemporaryViews(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003521 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003522 }
Selim Cinek1685e632014-04-08 02:27:49 +02003523 }
3524
Selim Cineka5703182016-05-11 21:23:16 -04003525 private void clearTemporaryViews(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003526 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003527 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003528 }
Selim Cineka5703182016-05-11 21:23:16 -04003529 if (viewGroup != null) {
3530 viewGroup.getOverlay().clear();
3531 }
Selim Cinekd1395642016-04-28 12:22:42 -07003532 }
3533
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003534 public void onPanelTrackingStarted() {
3535 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003536 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003537 }
3538 public void onPanelTrackingStopped() {
3539 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003540 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003541 }
3542
Selim Cinekb24e0a92015-06-09 20:17:30 -07003543 public void resetScrollPosition() {
3544 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003545 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003546 }
3547
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003548 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003549 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003550 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003551 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003552 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003553 if (!mIsExpanded) {
3554 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003555 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08003556 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003557 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003558 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003559 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003560 }
3561 }
3562
3563 private void updateChronometers() {
3564 int childCount = getChildCount();
3565 for (int i = 0; i < childCount; i++) {
3566 updateChronometerForChild(getChildAt(i));
3567 }
3568 }
3569
3570 private void updateChronometerForChild(View child) {
3571 if (child instanceof ExpandableNotificationRow) {
3572 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3573 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003574 }
Selim Cinek1685e632014-04-08 02:27:49 +02003575 }
3576
Jorim Jaggibe565df2014-04-28 17:51:23 +02003577 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003578 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003579 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003580 updateScrollPositionOnExpandInBottom(view);
3581 clampScrollPosition();
Selim Cinekaef92ef2014-06-06 18:06:04 +02003582 notifyHeightChangeListener(view);
Selim Cinekbc243a92016-09-27 16:35:13 -07003583 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3584 ? (ExpandableNotificationRow) view
3585 : null;
3586 if (row != null && (row == mFirstVisibleBackgroundChild
3587 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3588 updateAlgorithmLayoutMinHeight();
3589 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003590 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003591 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003592 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003593 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003594 }
3595
Selim Cineka5e211b2014-08-11 17:35:48 +02003596 @Override
3597 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003598 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003599 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003600 }
3601
Selim Cinekf7a14c02014-07-07 14:01:46 +02003602 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07003603 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02003604 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003605 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003606 if (row.isSummaryWithChildren()) {
3607 return;
3608 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003609 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003610 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003611 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003612 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003613 }
Selim Cinekdb167372016-11-17 15:41:17 -08003614 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Anthony Chen9e05d462017-04-07 10:10:21 -07003615 if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08003616 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3617 }
3618 if (endPosition > layoutEnd) {
3619 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003620 mDisallowScrollingInThisMotion = true;
3621 }
3622 }
3623 }
3624 }
3625
Jorim Jaggibe565df2014-04-28 17:51:23 +02003626 public void setOnHeightChangedListener(
3627 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3628 this.mOnHeightChangedListener = mOnHeightChangedListener;
3629 }
3630
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003631 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3632 mOnEmptySpaceClickListener = listener;
3633 }
3634
Selim Cinek572bbd42014-04-25 16:43:27 +02003635 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003636 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003637 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003638 runAnimationFinishedRunnables();
Selim Cinek0fccc722015-07-29 17:04:36 -07003639 clearViewOverlays();
Selim Cinek8fc78752016-07-13 14:34:56 -07003640 clearHeadsUpDisappearRunning();
3641 }
3642
3643 private void clearHeadsUpDisappearRunning() {
3644 for (int i = 0; i < getChildCount(); i++) {
3645 View view = getChildAt(i);
3646 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003647 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003648 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003649 if (row.isSummaryWithChildren()) {
3650 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003651 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003652 }
3653 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003654 }
3655 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003656 }
3657
3658 private void clearViewOverlays() {
3659 for (View view : mClearOverlayViewsWhenFinished) {
Selim Cineka5703182016-05-11 21:23:16 -04003660 StackStateAnimator.removeFromOverlay(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003661 }
Selim Cinekc8df0642017-02-23 17:01:04 -08003662 mClearOverlayViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003663 }
3664
3665 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003666 for (Runnable runnable : mAnimationFinishedRunnables) {
3667 runnable.run();
3668 }
3669 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003670 }
3671
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003672 /**
3673 * See {@link AmbientState#setDimmed}.
3674 */
3675 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07003676 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003677 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003678 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003679 mDimmedNeedsAnimation = true;
3680 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003681 animateDimmed(dimmed);
3682 } else {
3683 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003684 }
3685 requestChildrenUpdate();
3686 }
3687
Selim Cinek8a9308d2017-08-24 09:31:08 -07003688 @VisibleForTesting
3689 boolean isDimmed() {
3690 return mAmbientState.isDimmed();
3691 }
3692
Selim Cinekd35c2792016-01-21 13:20:57 -08003693 private void setDimAmount(float dimAmount) {
3694 mDimAmount = dimAmount;
3695 updateBackgroundDimming();
3696 }
3697
3698 private void animateDimmed(boolean dimmed) {
3699 if (mDimAnimator != null) {
3700 mDimAnimator.cancel();
3701 }
3702 float target = dimmed ? 1.0f : 0.0f;
3703 if (target == mDimAmount) {
3704 return;
3705 }
3706 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3707 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3708 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3709 mDimAnimator.addListener(mDimEndListener);
3710 mDimAnimator.addUpdateListener(mDimUpdateListener);
3711 mDimAnimator.start();
3712 }
3713
Jorim Jaggiae441282014-08-01 02:45:18 +02003714 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3715 if (hideSensitive != mAmbientState.isHideSensitive()) {
3716 int childCount = getChildCount();
3717 for (int i = 0; i < childCount; i++) {
3718 ExpandableView v = (ExpandableView) getChildAt(i);
3719 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3720 }
3721 mAmbientState.setHideSensitive(hideSensitive);
3722 if (animate && mAnimationsEnabled) {
3723 mHideSensitiveNeedsAnimation = true;
3724 mNeedsAnimation = true;
3725 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08003726 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02003727 requestChildrenUpdate();
3728 }
3729 }
3730
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003731 /**
3732 * See {@link AmbientState#setActivatedChild}.
3733 */
Selim Cineka32ab602014-06-11 15:06:01 +02003734 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003735 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003736 if (mAnimationsEnabled) {
3737 mActivateNeedsAnimation = true;
3738 mNeedsAnimation = true;
3739 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003740 requestChildrenUpdate();
3741 }
3742
Selim Cineka32ab602014-06-11 15:06:01 +02003743 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003744 return mAmbientState.getActivatedChild();
3745 }
3746
Selim Cinek572bbd42014-04-25 16:43:27 +02003747 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003748 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003749 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003750 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02003751 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003752 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003753 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003754 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003755 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003756 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08003757 }
3758
3759 private void updateViewShadows() {
3760 // we need to work around an issue where the shadow would not cast between siblings when
3761 // their z difference is between 0 and 0.1
3762
3763 // Lefts first sort by Z difference
3764 for (int i = 0; i < getChildCount(); i++) {
3765 ExpandableView child = (ExpandableView) getChildAt(i);
3766 if (child.getVisibility() != GONE) {
3767 mTmpSortedChildren.add(child);
3768 }
3769 }
3770 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3771
3772 // Now lets update the shadow for the views
3773 ExpandableView previous = null;
3774 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3775 ExpandableView expandableView = mTmpSortedChildren.get(i);
3776 float translationZ = expandableView.getTranslationZ();
3777 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3778 float diff = otherZ - translationZ;
3779 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3780 // There is no fake shadow to be drawn
3781 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3782 } else {
3783 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003784 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3785 expandableView.setFakeShadowIntensity(
3786 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003787 previous.getOutlineAlpha(), (int) yLocation,
3788 previous.getOutlineTranslation());
3789 }
3790 previous = expandableView;
3791 }
3792
3793 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003794 }
3795
Lucas Dupine17ce522017-07-17 15:45:06 -07003796 /**
3797 * Update colors of "dismiss" and "empty shade" views.
3798 *
3799 * @param lightTheme True if light theme should be used.
3800 */
3801 public void updateDecorViews(boolean lightTheme) {
3802 if (lightTheme == mUsingLightTheme) {
3803 return;
3804 }
3805 mUsingLightTheme = lightTheme;
3806 Context context = new ContextThemeWrapper(mContext,
3807 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
3808 final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
3809 mDismissView.setTextColor(textColor);
3810 mEmptyShadeView.setTextColor(textColor);
3811 }
3812
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003813 public void goToFullShade(long delay) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003814 if (mDismissView != null) {
3815 mDismissView.setInvisible();
3816 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003817 mEmptyShadeView.setInvisible();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003818 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003819 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003820 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003821 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003822 }
3823
Selim Cinek1408eb52014-06-02 14:45:38 +02003824 public void cancelExpandHelper() {
3825 mExpandHelper.cancel();
3826 }
3827
3828 public void setIntrinsicPadding(int intrinsicPadding) {
3829 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07003830 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02003831 }
3832
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003833 public int getIntrinsicPadding() {
3834 return mIntrinsicPadding;
3835 }
3836
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003837 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003838 * @return the y position of the first notification
3839 */
3840 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003841 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003842 }
3843
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003844 @Override
3845 public boolean shouldDelayChildPressedState() {
3846 return true;
3847 }
3848
Jorim Jaggi457cc352014-06-02 22:47:42 +02003849 /**
John Spurlockbf370992014-06-17 13:58:31 -04003850 * See {@link AmbientState#setDark}.
3851 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003852 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02003853 if (mAmbientState.isDark() == dark) {
3854 return;
3855 }
John Spurlockbf370992014-06-17 13:58:31 -04003856 mAmbientState.setDark(dark);
3857 if (animate && mAnimationsEnabled) {
3858 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003859 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
John Spurlockbf370992014-06-17 13:58:31 -04003860 mNeedsAnimation = true;
Lucas Dupind285cf02018-01-18 09:18:23 -08003861 setDarkAmount(0.0f);
Selim Cinek972123d2016-05-03 14:25:58 -07003862 } else if (!dark) {
Lucas Dupind285cf02018-01-18 09:18:23 -08003863 setDarkAmount(1.0f);
John Spurlockbf370992014-06-17 13:58:31 -04003864 }
3865 requestChildrenUpdate();
Selim Cinek6811d722016-01-19 17:53:12 -08003866 if (dark) {
Selim Cinek6811d722016-01-19 17:53:12 -08003867 mScrimController.setExcludedBackgroundArea(null);
3868 } else {
3869 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08003870 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003871
3872 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003873 updateContentHeight();
3874 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04003875 }
3876
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003877 /**
3878 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
3879 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
3880 * {@link #mAmbientState}'s dark mode is toggled.
3881 */
3882 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08003883 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07003884 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003885 }
3886
Lucas Dupind285cf02018-01-18 09:18:23 -08003887 private void setDarkAmount(float darkAmount) {
3888 mDarkAmount = darkAmount;
Selim Cinek972123d2016-05-03 14:25:58 -07003889 updateBackgroundDimming();
3890 }
3891
Lucas Dupind285cf02018-01-18 09:18:23 -08003892 public float getDarkAmount() {
3893 return mDarkAmount;
Selim Cinek972123d2016-05-03 14:25:58 -07003894 }
3895
3896 private void startBackgroundFadeIn() {
Lucas Dupind285cf02018-01-18 09:18:23 -08003897 ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, 0f, 1f);
Adrian Roos28f90c72017-05-08 17:24:26 -07003898 fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
Selim Cinek972123d2016-05-03 14:25:58 -07003899 fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
3900 fadeAnimator.start();
3901 }
3902
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003903 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07003904 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003905 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3906 }
3907 if (screenLocation.y > getBottomMostNotificationBottom()) {
3908 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
3909 }
3910 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
3911 if (child != null) {
3912 return getNotGoneIndex(child);
3913 } else {
3914 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3915 }
3916 }
3917
3918 private int getNotGoneIndex(View child) {
3919 int count = getChildCount();
3920 int notGoneIndex = 0;
3921 for (int i = 0; i < count; i++) {
3922 View v = getChildAt(i);
3923 if (child == v) {
3924 return notGoneIndex;
3925 }
3926 if (v.getVisibility() != View.GONE) {
3927 notGoneIndex++;
3928 }
3929 }
3930 return -1;
3931 }
3932
Anthony Chen5e3742e2017-04-07 14:28:44 -07003933 public void setDismissView(@NonNull DismissView dismissView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003934 int index = -1;
3935 if (mDismissView != null) {
3936 index = indexOfChild(mDismissView);
3937 removeView(mDismissView);
3938 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003939 mDismissView = dismissView;
Selim Cinek01af3342016-02-09 19:25:31 -08003940 addView(mDismissView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003941 }
3942
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003943 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003944 int index = -1;
3945 if (mEmptyShadeView != null) {
3946 index = indexOfChild(mEmptyShadeView);
3947 removeView(mEmptyShadeView);
3948 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003949 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08003950 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003951 }
3952
3953 public void updateEmptyShadeView(boolean visible) {
3954 int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
3955 int newVisibility = visible ? VISIBLE : GONE;
3956 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003957 if (newVisibility != GONE) {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003958 if (mEmptyShadeView.willBeGone()) {
3959 mEmptyShadeView.cancelAnimation();
3960 } else {
3961 mEmptyShadeView.setInvisible();
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003962 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003963 mEmptyShadeView.setVisibility(newVisibility);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003964 mEmptyShadeView.setWillBeGone(false);
3965 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003966 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003967 } else {
Selim Cinek20867102014-12-10 17:09:17 +01003968 Runnable onFinishedRunnable = new Runnable() {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003969 @Override
3970 public void run() {
3971 mEmptyShadeView.setVisibility(GONE);
3972 mEmptyShadeView.setWillBeGone(false);
3973 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003974 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003975 }
Selim Cinek20867102014-12-10 17:09:17 +01003976 };
Selim Cineka9d3efd2016-08-16 14:16:59 -07003977 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek20867102014-12-10 17:09:17 +01003978 mEmptyShadeView.setWillBeGone(true);
3979 mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
3980 } else {
3981 mEmptyShadeView.setInvisible();
3982 onFinishedRunnable.run();
3983 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003984 }
3985 }
3986 }
3987
Dan Sandlereceda3d2014-07-21 15:35:01 -04003988 public void updateDismissView(boolean visible) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003989 if (mDismissView == null) {
3990 return;
3991 }
3992
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003993 int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003994 int newVisibility = visible ? VISIBLE : GONE;
3995 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003996 if (newVisibility != GONE) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003997 if (mDismissView.willBeGone()) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003998 mDismissView.cancelAnimation();
3999 } else {
4000 mDismissView.setInvisible();
Dan Sandlereceda3d2014-07-21 15:35:01 -04004001 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02004002 mDismissView.setVisibility(newVisibility);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004003 mDismissView.setWillBeGone(false);
4004 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02004005 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004006 } else {
Selim Cinek7d5f3742014-11-07 18:07:49 +01004007 Runnable dimissHideFinishRunnable = new Runnable() {
Dan Sandlereceda3d2014-07-21 15:35:01 -04004008 @Override
4009 public void run() {
4010 mDismissView.setVisibility(GONE);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004011 mDismissView.setWillBeGone(false);
4012 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02004013 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004014 }
Selim Cinek7d5f3742014-11-07 18:07:49 +01004015 };
Selim Cinek20867102014-12-10 17:09:17 +01004016 if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
Selim Cinek7d5f3742014-11-07 18:07:49 +01004017 mDismissView.setWillBeGone(true);
4018 mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
4019 } else {
4020 dimissHideFinishRunnable.run();
Selim Cinek7d5f3742014-11-07 18:07:49 +01004021 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04004022 }
4023 }
4024 }
4025
4026 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4027 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004028 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004029 handleDismissAllClipping();
4030 }
4031
4032 private void handleDismissAllClipping() {
4033 final int count = getChildCount();
4034 boolean previousChildWillBeDismissed = false;
4035 for (int i = 0; i < count; i++) {
4036 ExpandableView child = (ExpandableView) getChildAt(i);
4037 if (child.getVisibility() == GONE) {
4038 continue;
4039 }
4040 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4041 child.setMinClipTopAmount(child.getClipTopAmount());
4042 } else {
4043 child.setMinClipTopAmount(0);
4044 }
4045 previousChildWillBeDismissed = canChildBeDismissed(child);
4046 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004047 }
4048
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004049 public boolean isDismissViewNotGone() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004050 return mDismissView != null
4051 && mDismissView.getVisibility() != View.GONE
4052 && !mDismissView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004053 }
4054
4055 public boolean isDismissViewVisible() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004056 return mDismissView != null && mDismissView.isVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004057 }
4058
4059 public int getDismissViewHeight() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004060 return mDismissView == null ? 0 : mDismissView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004061 }
4062
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004063 public int getEmptyShadeViewHeight() {
4064 return mEmptyShadeView.getHeight();
4065 }
4066
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004067 public float getBottomMostNotificationBottom() {
4068 final int count = getChildCount();
4069 float max = 0;
4070 for (int childIdx = 0; childIdx < count; childIdx++) {
4071 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4072 if (child.getVisibility() == GONE) {
4073 continue;
4074 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004075 float bottom = child.getTranslationY() + child.getActualHeight()
4076 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004077 if (bottom > max) {
4078 max = bottom;
4079 }
4080 }
Selim Cinekd2281152015-04-10 14:37:46 -07004081 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004082 }
4083
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004084 public void setStatusBar(StatusBar statusBar) {
4085 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004086 }
4087
Selim Cinekb5605e52015-02-20 18:21:41 +01004088 public void setGroupManager(NotificationGroupManager groupManager) {
4089 this.mGroupManager = groupManager;
4090 }
4091
Selim Cinekd9acca52014-09-01 22:33:25 +02004092 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01004093 requestAnimateEverything();
4094 }
4095
4096 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004097 if (mIsExpanded && mAnimationsEnabled) {
4098 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004099 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004100 requestChildrenUpdate();
4101 }
4102 }
4103
Selim Cinek04fb2582015-06-02 19:58:09 +02004104 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004105 int childCount = getChildCount();
4106 for (int i = childCount - 1; i >= 0; i--) {
4107 ExpandableView child = (ExpandableView) getChildAt(i);
4108 if (child.getVisibility() != View.GONE) {
4109 float childTop = child.getY();
4110 if (childTop > touchY) {
4111 // we are above a notification entirely let's abort
4112 return false;
4113 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004114 boolean belowChild = touchY > childTop + child.getActualHeight()
4115 - child.getClipBottomAmount();
Selim Cinekabf60bb2015-02-20 17:36:10 +01004116 if (child == mDismissView) {
4117 if(!belowChild && !mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
4118 touchY - childTop)) {
4119 // We clicked on the dismiss button
4120 return false;
4121 }
4122 } else if (child == mEmptyShadeView) {
4123 // We arrived at the empty shade view, for which we accept all clicks
4124 return true;
4125 } else if (!belowChild){
4126 // We are on a child
4127 return false;
4128 }
4129 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004130 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004131 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004132 }
4133
Selim Cinekb5605e52015-02-20 18:21:41 +01004134 @Override
4135 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004136 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4137 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004138 if (animated) {
4139 mExpandedGroupView = changedRow;
4140 mNeedsAnimation = true;
4141 }
4142 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004143 if (!mGroupExpandedForMeasure) {
4144 onHeightChanged(changedRow, false /* needsAnimation */);
4145 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004146 runAfterAnimationFinished(new Runnable() {
4147 @Override
4148 public void run() {
4149 changedRow.onFinishedExpansionChange();
4150 }
4151 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004152 }
4153
4154 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004155 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004156 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004157 }
4158
Selim Cinekc22fff62016-05-20 12:44:30 -07004159 /** @hide */
4160 @Override
4161 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4162 super.onInitializeAccessibilityEventInternal(event);
4163 event.setScrollable(mScrollable);
4164 event.setScrollX(mScrollX);
4165 event.setScrollY(mOwnScrollY);
4166 event.setMaxScrollX(mScrollX);
4167 event.setMaxScrollY(getScrollRange());
4168 }
4169
4170 @Override
4171 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4172 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004173 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004174 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004175 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004176 info.addAction(
4177 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4178 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4179 }
Selim Cinekef406062016-09-29 17:33:13 -07004180 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004181 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4182 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4183 }
4184 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004185 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4186 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004187 }
4188
4189 /** @hide */
4190 @Override
4191 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4192 if (super.performAccessibilityActionInternal(action, arguments)) {
4193 return true;
4194 }
4195 if (!isEnabled()) {
4196 return false;
4197 }
4198 int direction = -1;
4199 switch (action) {
4200 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4201 // fall through
4202 case android.R.id.accessibilityActionScrollDown:
4203 direction = 1;
4204 // fall through
4205 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4206 // fall through
4207 case android.R.id.accessibilityActionScrollUp:
4208 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004209 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004210 final int targetScrollY = Math.max(0,
4211 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4212 if (targetScrollY != mOwnScrollY) {
4213 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004214 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004215 return true;
4216 }
4217 break;
4218 }
4219 return false;
4220 }
4221
Selim Cinekef5127e2015-12-21 16:55:58 -08004222 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004223 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004224 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004225 }
4226
4227 public void generateChildOrderChangedEvent() {
4228 if (mIsExpanded && mAnimationsEnabled) {
4229 mGenerateChildOrderChangedEvent = true;
4230 mNeedsAnimation = true;
4231 requestChildrenUpdate();
4232 }
4233 }
4234
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004235 @Override
4236 public int getContainerChildCount() {
4237 return getChildCount();
4238 }
4239
4240 @Override
4241 public View getContainerChildAt(int i) {
4242 return getChildAt(i);
4243 }
4244
4245 @Override
4246 public void removeContainerView(View v) {
4247 removeView(v);
4248 }
4249
4250 @Override
4251 public void addContainerView(View v) {
4252 addView(v);
4253 }
4254
Selim Cinek684a4422015-04-15 16:18:39 -07004255 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004256 mAnimationFinishedRunnables.add(runnable);
4257 }
4258
4259 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
4260 mHeadsUpManager = headsUpManager;
4261 mAmbientState.setHeadsUpManager(headsUpManager);
4262 }
4263
4264 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004265 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004266 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4267 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004268 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004269 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004270 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004271 requestChildrenUpdate();
4272 }
4273 }
4274
4275 public void setShadeExpanded(boolean shadeExpanded) {
4276 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004277 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004278 }
4279
Selim Cineka59ecc32015-04-07 10:51:49 -07004280 /**
4281 * Set the boundary for the bottom heads up position. The heads up will always be above this
4282 * position.
4283 *
4284 * @param height the height of the screen
4285 * @param bottomBarHeight the height of the bar on the bottom
4286 */
4287 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4288 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4289 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004290 requestChildrenUpdate();
4291 }
4292
4293 public void setTrackingHeadsUp(boolean trackingHeadsUp) {
4294 mTrackingHeadsUp = trackingHeadsUp;
4295 }
4296
Selim Cinekaac93252015-04-14 20:04:12 -07004297 public void setScrimController(ScrimController scrimController) {
4298 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004299 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004300 }
4301
Selim Cinekbbc580b2015-06-03 14:11:03 +02004302 public void forceNoOverlappingRendering(boolean force) {
4303 mForceNoOverlappingRendering = force;
4304 }
4305
4306 @Override
4307 public boolean hasOverlappingRendering() {
4308 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4309 }
4310
Selim Cinek6811d722016-01-19 17:53:12 -08004311 public void setAnimationRunning(boolean animationRunning) {
4312 if (animationRunning != mAnimationRunning) {
4313 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004314 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004315 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004316 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004317 }
4318 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004319 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004320 }
4321 }
4322
Selim Cinek3776fe02016-02-04 13:32:43 -08004323 public boolean isExpanded() {
4324 return mIsExpanded;
4325 }
4326
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004327 public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) {
Adrian Roos7d062c42017-03-30 15:11:43 -07004328 if (mPulsing == null && pulsing == null) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004329 return;
4330 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004331 mPulsing = pulsing;
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004332 mClockBottom = clockBottom;
Selim Cinekebf42342017-07-13 15:46:10 +02004333 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004334 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004335 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004336 updateContentHeight();
4337 notifyHeightChangeListener(mShelf);
Adrian Roosd83e9992017-03-16 15:17:57 -07004338 requestChildrenUpdate();
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004339 }
4340
Selim Cinek07304f5222016-05-19 18:31:36 -07004341 public void setFadingOut(boolean fadingOut) {
4342 if (fadingOut != mFadingOut) {
4343 mFadingOut = fadingOut;
4344 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004345 }
4346 }
4347
Selim Cinek48ff9b42016-11-09 19:31:51 -08004348 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4349 if (mScrimController == null) {
4350 // we're not set up yet.
4351 return;
4352 }
4353 if (parentNotFullyVisible != mParentNotFullyVisible) {
4354 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004355 updateFadingState();
4356 }
4357 }
4358
4359 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004360 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004361 updateSrcDrawing();
4362 }
4363
Selim Cinek31d37b92016-04-26 09:56:42 -07004364 @Override
4365 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4366 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004367 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004368 }
4369
Selim Cinekbc243a92016-09-27 16:35:13 -07004370 public void setQsExpanded(boolean qsExpanded) {
4371 mQsExpanded = qsExpanded;
4372 updateAlgorithmLayoutMinHeight();
4373 }
4374
Selim Cinekef406062016-09-29 17:33:13 -07004375 public void setOwnScrollY(int ownScrollY) {
4376 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004377 // We still want to call the normal scrolled changed for accessibility reasons
4378 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004379 mOwnScrollY = ownScrollY;
4380 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004381 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004382 }
4383 }
4384
Selim Cinek281c2022016-10-13 19:14:43 -07004385 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004386 int index = -1;
4387 if (mShelf != null) {
4388 index = indexOfChild(mShelf);
4389 removeView(mShelf);
4390 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004391 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004392 addView(mShelf, index);
4393 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004394 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004395 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004396 }
4397
4398 public NotificationShelf getNotificationShelf() {
4399 return mShelf;
4400 }
4401
Selim Cinekad7fac02016-10-18 17:09:15 -07004402 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4403 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4404 mMaxDisplayedNotifications = maxDisplayedNotifications;
4405 updateContentHeight();
4406 notifyHeightChangeListener(mShelf);
4407 }
4408 }
4409
Selim Cinek48ff9b42016-11-09 19:31:51 -08004410 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004411 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004412 }
4413
Selim Cinekcafa87f2016-10-26 17:00:17 -07004414 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4415 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4416 updateClipping();
4417 }
4418
4419 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4420 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4421 updateClipping();
4422 }
4423
Selim Cinek355652a2016-12-07 13:32:12 -08004424 public void setStatusBarState(int statusBarState) {
4425 mStatusBarState = statusBarState;
4426 mAmbientState.setStatusBarState(statusBarState);
4427 }
4428
Selim Cinekd5ab6452016-12-08 16:34:00 -08004429 public void setExpandingVelocity(float expandingVelocity) {
4430 mAmbientState.setExpandingVelocity(expandingVelocity);
4431 }
4432
Selim Cinekfcff4c62016-12-27 14:26:06 +01004433 public float getOpeningHeight() {
4434 if (mEmptyShadeView.getVisibility() == GONE) {
4435 return getMinExpansionHeight();
4436 } else {
4437 return getAppearEndPosition();
4438 }
4439 }
4440
4441 public void setIsFullWidth(boolean isFullWidth) {
4442 mAmbientState.setPanelFullWidth(isFullWidth);
4443 }
4444
Selim Cinekec29d342017-05-05 18:31:49 -07004445 public void setUnlockHintRunning(boolean running) {
4446 mAmbientState.setUnlockHintRunning(running);
4447 }
4448
Selim Cinek5cf1d052017-06-01 17:36:46 -07004449 public void setQsCustomizerShowing(boolean isShowing) {
4450 mAmbientState.setQsCustomizerShowing(isShowing);
4451 requestChildrenUpdate();
4452 }
4453
4454 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4455 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4456 }
4457
Adrian Roosdc747bd2017-06-01 16:09:15 -07004458 public void setDarkShelfOffsetX(int shelfOffsetX) {
4459 mShelf.setDarkOffsetX(shelfOffsetX);
4460 }
4461
Selim Cinek707e2072017-06-30 18:32:40 +02004462 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4463 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
4464 + " alpha:%f scrollY:%d]",
4465 this.getClass().getSimpleName(),
4466 mPulsing != null ?"T":"f",
4467 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4468 getVisibility() == View.VISIBLE ? "visible"
4469 : getVisibility() == View.GONE ? "gone"
4470 : "invisible",
4471 getAlpha(),
4472 mAmbientState.getScrollY()));
4473 }
4474
Selim Cinek7103fd42016-05-09 22:22:33 -04004475 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004476 * A listener that is notified when the empty space below the notifications is clicked on
4477 */
4478 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004479 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004480 }
4481
4482 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004483 * A listener that gets notified when the overscroll at the top has changed.
4484 */
4485 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004486
4487 /**
4488 * Notifies a listener that the overscroll has changed.
4489 *
4490 * @param amount the amount of overscroll, in pixels
4491 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4492 * unrubberbanded motion to directly expand overscroll view (e.g expand
4493 * QS)
4494 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004495 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004496
4497 /**
4498 * Notify a listener that the scroller wants to escape from the scrolling motion and
4499 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4500 *
4501 * @param velocity The velocity that the Scroller had when over flinging
4502 * @param open Should the fling open or close the overscroll view.
4503 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004504 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004505 }
4506
Mady Mellor95d743c2017-01-10 12:05:27 -08004507 private class NotificationSwipeHelper extends SwipeHelper
4508 implements NotificationSwipeActionHelper {
4509 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004510 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004511 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004512
4513 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4514 super(swipeDirection, callback, context);
4515 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004516 mFalsingCheck = new Runnable() {
4517 @Override
4518 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004519 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004520 }
4521 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004522 }
4523
4524 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004525 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004526 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004527 if (mCurrMenuRow != null) {
4528 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004529 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004530 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004531 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004532
Mady Mellor95d743c2017-01-10 12:05:27 -08004533 // Slide back any notifications that might be showing a menu
4534 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004535
4536 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004537 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
4538 mCurrMenuRow = row.createMenu();
4539 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4540 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor89e15ec2017-06-28 17:08:21 -07004541 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004542 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004543 }
4544
4545 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004546 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004547 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004548 if (mCurrMenuRow != null) {
4549 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004550 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004551 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004552
Mady Mellor95d743c2017-01-10 12:05:27 -08004553 @Override
4554 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4555 float translation) {
4556 if (mCurrMenuRow != null) {
4557 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004558 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004559 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004560 }
4561
4562 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004563 public void dismissChild(final View view, float velocity,
4564 boolean useAccelerateInterpolator) {
4565 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004566 if (mIsExpanded) {
4567 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4568 // of the panel early.
4569 handleChildDismissed(view);
4570 }
Eliot Courtney47098cb2017-10-18 17:30:30 +09004571 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
4572 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4573 false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004574 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004575 }
4576
4577 @Override
4578 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004579 super.snapChild(animView, targetLeft, velocity);
4580 onDragCancelled(animView);
4581 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004582 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004583 }
4584 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004585
4586 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004587 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4588 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004589 }
4590
Mady Mellorbd707492017-05-10 17:51:25 -07004591 public boolean isFalseGesture(MotionEvent ev) {
4592 return super.isFalseGesture(ev);
4593 }
4594
Mady Mellor95d743c2017-01-10 12:05:27 -08004595 private void handleMenuCoveredOrDismissed() {
4596 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4597 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004598 }
4599 }
4600
Mady Mellor4b80b102016-01-22 08:03:58 -08004601 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004602 public Animator getViewTranslationAnimator(View v, float target,
4603 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004604 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004605 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4606 } else {
4607 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004608 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004609 }
4610
4611 @Override
4612 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004613 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004614 }
4615
4616 @Override
4617 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004618 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004619 }
4620
Mady Mellor95d743c2017-01-10 12:05:27 -08004621 @Override
4622 public void dismiss(View animView, float velocity) {
4623 dismissChild(animView, velocity,
4624 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4625 }
4626
4627 @Override
4628 public void snap(View animView, float targetLeft, float velocity) {
4629 snapChild(animView, targetLeft, velocity);
4630 }
4631
4632 @Override
4633 public boolean swipedFarEnough(float translation, float viewSize) {
4634 return swipedFarEnough();
4635 }
4636
4637 @Override
4638 public boolean swipedFastEnough(float translation, float velocity) {
4639 return swipedFastEnough();
4640 }
4641
4642 @Override
4643 public float getMinDismissVelocity() {
4644 return getEscapeVelocity();
4645 }
4646
4647 public void onMenuShown(View animView) {
4648 onDragCancelled(animView);
4649
4650 // If we're on the lockscreen we want to false this.
4651 if (isAntiFalsingNeeded()) {
4652 mHandler.removeCallbacks(mFalsingCheck);
4653 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4654 }
4655 }
4656
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004657 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09004658 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004659 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004660 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4661 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004662 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004663 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004664 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004665 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004666 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004667 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004668 if (view != null && !isTouchInView(ev, view)) {
4669 // Touch was outside visible guts / menu notification, close what's visible
Eliot Courtney47098cb2017-10-18 17:30:30 +09004670 mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
4671 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4672 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004673 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004674 }
4675 }
4676
Mady Mellor95d743c2017-01-10 12:05:27 -08004677 public void resetExposedMenuView(boolean animate, boolean force) {
4678 if (mMenuExposedView == null
4679 || (!force && mMenuExposedView == mTranslatingParentView)) {
4680 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004681 return;
4682 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004683 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004684 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004685 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004686 0 /* leftTarget */, null /* updateListener */);
4687 if (anim != null) {
4688 anim.start();
4689 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004690 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
Selim Cinekb2e0f332017-08-18 12:24:38 -07004691 ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
4692 if (!row.isRemoved()) {
4693 row.resetTranslation();
4694 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004695 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004696 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004697 }
4698 }
4699
Mady Mellorc2dbe492017-03-30 13:22:03 -07004700 private boolean isTouchInView(MotionEvent ev, View view) {
4701 if (view == null) {
4702 return false;
4703 }
4704 final int height = (view instanceof ExpandableView)
4705 ? ((ExpandableView) view).getActualHeight()
4706 : view.getHeight();
4707 final int rx = (int) ev.getRawX();
4708 final int ry = (int) ev.getRawY();
4709 view.getLocationOnScreen(mTempInt2);
4710 final int x = mTempInt2[0];
4711 final int y = mTempInt2[1];
4712 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4713 boolean ret = rect.contains(rx, ry);
4714 return ret;
4715 }
4716
Selim Cinek33223572016-02-19 19:32:22 -08004717 private void updateContinuousShadowDrawing() {
4718 boolean continuousShadowUpdate = mAnimationRunning
4719 || !mAmbientState.getDraggedViews().isEmpty();
4720 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4721 if (continuousShadowUpdate) {
4722 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4723 } else {
4724 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4725 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004726 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004727 }
4728 }
4729
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004730 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004731 public void resetExposedMenuView(boolean animate, boolean force) {
4732 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004733 }
4734
4735 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4736 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4737 }
4738
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004739 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004740
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004741 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4742
4743 // ANIMATION_TYPE_ADD
4744 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004745 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004746 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004747 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004748 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004749 .animateZ()
4750 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004751
4752 // ANIMATION_TYPE_REMOVE
4753 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004754 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004755 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004756 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004757 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004758 .animateZ()
4759 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004760
4761 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4762 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004763 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004764 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004765 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004766 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004767 .animateZ()
4768 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004769
4770 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4771 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004772 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004773 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004774 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004775 .animateY()
4776 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004777 .animateZ(),
4778
4779 // ANIMATION_TYPE_START_DRAG
4780 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004781 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004782
4783 // ANIMATION_TYPE_SNAP_BACK
4784 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004785 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004786 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004787
4788 // ANIMATION_TYPE_ACTIVATED_CHILD
4789 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004790 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004791
4792 // ANIMATION_TYPE_DIMMED
4793 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004794 .animateDimmed(),
4795
4796 // ANIMATION_TYPE_CHANGE_POSITION
4797 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004798 .animateAlpha() // maybe the children change positions
4799 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004800 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004801 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004802 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004803 .animateZ(),
4804
4805 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004806 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004807
4808 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4809 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004810 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004811 .animateHeight()
4812 .animateTopInset()
4813 .animateY()
4814 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004815 .animateZ()
4816 .hasDelays(),
4817
4818 // ANIMATION_TYPE_HIDE_SENSITIVE
4819 new AnimationFilter()
4820 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02004821
4822 // ANIMATION_TYPE_VIEW_RESIZE
4823 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004824 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02004825 .animateHeight()
4826 .animateTopInset()
4827 .animateY()
4828 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02004829
Selim Cinekb5605e52015-02-20 18:21:41 +01004830 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
4831 new AnimationFilter()
4832 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004833 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01004834 .animateHeight()
4835 .animateTopInset()
4836 .animateY()
4837 .animateZ(),
4838
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004839 // ANIMATION_TYPE_HEADS_UP_APPEAR
4840 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004841 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004842 .animateHeight()
4843 .animateTopInset()
4844 .animateY()
4845 .animateZ(),
4846
4847 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4848 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004849 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004850 .animateHeight()
4851 .animateTopInset()
4852 .animateY()
4853 .animateZ(),
4854
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004855 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4856 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004857 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004858 .animateHeight()
4859 .animateTopInset()
4860 .animateY()
4861 .animateZ()
4862 .hasDelays(),
4863
Selim Cineka59ecc32015-04-07 10:51:49 -07004864 // ANIMATION_TYPE_HEADS_UP_OTHER
4865 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004866 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07004867 .animateHeight()
4868 .animateTopInset()
4869 .animateY()
4870 .animateZ(),
4871
Selim Cinekd9acca52014-09-01 22:33:25 +02004872 // ANIMATION_TYPE_EVERYTHING
4873 new AnimationFilter()
4874 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004875 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02004876 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02004877 .animateDimmed()
4878 .animateHideSensitive()
4879 .animateHeight()
4880 .animateTopInset()
4881 .animateY()
4882 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004883 };
4884
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004885 static int[] LENGTHS = new int[] {
4886
4887 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004888 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004889
4890 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004891 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004892
4893 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4894 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4895
4896 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4897 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4898
4899 // ANIMATION_TYPE_START_DRAG
4900 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4901
4902 // ANIMATION_TYPE_SNAP_BACK
4903 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4904
4905 // ANIMATION_TYPE_ACTIVATED_CHILD
4906 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
4907
4908 // ANIMATION_TYPE_DIMMED
4909 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004910
4911 // ANIMATION_TYPE_CHANGE_POSITION
4912 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04004913
4914 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004915 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004916
4917 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4918 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02004919
4920 // ANIMATION_TYPE_HIDE_SENSITIVE
4921 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02004922
4923 // ANIMATION_TYPE_VIEW_RESIZE
4924 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02004925
Selim Cinekb5605e52015-02-20 18:21:41 +01004926 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08004927 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01004928
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004929 // ANIMATION_TYPE_HEADS_UP_APPEAR
4930 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
4931
4932 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4933 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4934
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004935 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4936 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4937
Selim Cineka59ecc32015-04-07 10:51:49 -07004938 // ANIMATION_TYPE_HEADS_UP_OTHER
4939 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4940
Selim Cinekd9acca52014-09-01 22:33:25 +02004941 // ANIMATION_TYPE_EVERYTHING
4942 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004943 };
4944
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004945 static final int ANIMATION_TYPE_ADD = 0;
4946 static final int ANIMATION_TYPE_REMOVE = 1;
4947 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
4948 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
4949 static final int ANIMATION_TYPE_START_DRAG = 4;
4950 static final int ANIMATION_TYPE_SNAP_BACK = 5;
4951 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
4952 static final int ANIMATION_TYPE_DIMMED = 7;
4953 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04004954 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004955 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02004956 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02004957 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01004958 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004959 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
4960 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004961 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
4962 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
4963 static final int ANIMATION_TYPE_EVERYTHING = 18;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004964
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004965 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
4966 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
4967
Selim Cinek572bbd42014-04-25 16:43:27 +02004968 final long eventStartTime;
4969 final View changingView;
4970 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004971 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004972 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004973 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004974 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07004975 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02004976
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004977 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004978 this(view, type, LENGTHS[type]);
4979 }
4980
Adrian Roos28f90c72017-05-08 17:24:26 -07004981 AnimationEvent(View view, int type, AnimationFilter filter) {
4982 this(view, type, LENGTHS[type], filter);
4983 }
4984
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004985 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07004986 this(view, type, length, FILTERS[type]);
4987 }
4988
4989 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02004990 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
4991 changingView = view;
4992 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004993 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07004994 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004995 }
4996
4997 /**
4998 * Combines the length of several animation events into a single value.
4999 *
5000 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005001 * @return The combined length. Depending on the event types, this might be the maximum of
5002 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005003 */
5004 static long combineLength(ArrayList<AnimationEvent> events) {
5005 long length = 0;
5006 int size = events.size();
5007 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005008 AnimationEvent event = events.get(i);
5009 length = Math.max(length, event.length);
5010 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
5011 return event.length;
5012 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005013 }
5014 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02005015 }
5016 }
Selim Cinek67b22602014-03-10 15:40:16 +01005017}