blob: e4338496fa5f366748efe124cb57bc32f1fc7e43 [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 Cinek614576e2016-01-20 10:54:09 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.animation.PropertyValuesHolder;
Selim Cinekd35c2792016-01-21 13:20:57 -080023import android.animation.TimeAnimator;
24import android.animation.ValueAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080025import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinek31d37b92016-04-26 09:56:42 -070026import android.annotation.FloatRange;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010027import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010028import android.content.Context;
29import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080030import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010031import android.graphics.Canvas;
Selim Cinek67b22602014-03-10 15:40:16 +010032import android.graphics.Paint;
Selim Cinek515b2032017-11-15 10:20:19 -080033import android.graphics.Path;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010034import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080035import android.graphics.PorterDuff;
36import android.graphics.PorterDuffXfermode;
37import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070038import android.os.Bundle;
Mady Mellor4b80b102016-01-22 08:03:58 -080039import android.os.Handler;
Mady Mellor95d743c2017-01-10 12:05:27 -080040import android.service.notification.StatusBarNotification;
Anthony Chen5e3742e2017-04-07 14:28:44 -070041import android.support.annotation.NonNull;
Selim Cinek8a9308d2017-08-24 09:31:08 -070042import android.support.annotation.VisibleForTesting;
Selim Cinek67b22602014-03-10 15:40:16 +010043import android.util.AttributeSet;
Selim Cinek972123d2016-05-03 14:25:58 -070044import android.util.FloatProperty;
Selim Cinek67b22602014-03-10 15:40:16 +010045import android.util.Log;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070046import android.util.Pair;
Selim Cinek972123d2016-05-03 14:25:58 -070047import android.util.Property;
Lucas Dupine17ce522017-07-17 15:45:06 -070048import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070049import android.view.InputDevice;
Selim Cinek67b22602014-03-10 15:40:16 +010050import android.view.MotionEvent;
51import android.view.VelocityTracker;
52import android.view.View;
53import android.view.ViewConfiguration;
54import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020055import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070056import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070057import android.view.accessibility.AccessibilityEvent;
58import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020059import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080060import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010061import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070062import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070063
Mady Mellora41587b2016-02-11 18:43:06 -080064import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010065import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Lucas Dupine17ce522017-07-17 15:45:06 -070066import com.android.settingslib.Utils;
Selim Cinek67b22602014-03-10 15:40:16 +010067import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080068import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010069import com.android.systemui.R;
70import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070071import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080072import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
73import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
74import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Selim Cineka32ab602014-06-11 15:06:01 +020075import com.android.systemui.statusbar.ActivatableNotificationView;
Dan Sandlereceda3d2014-07-21 15:35:01 -040076import com.android.systemui.statusbar.DismissView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020077import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010078import com.android.systemui.statusbar.ExpandableNotificationRow;
Selim Cinek515b2032017-11-15 10:20:19 -080079import com.android.systemui.statusbar.ExpandableOutlineView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020080import com.android.systemui.statusbar.ExpandableView;
Adrian Roos7d062c42017-03-30 15:11:43 -070081import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070082import com.android.systemui.statusbar.NotificationGuts;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090083import com.android.systemui.statusbar.NotificationListContainer;
84import com.android.systemui.statusbar.NotificationLogger;
Selim Cinek0cfbef42016-11-09 19:06:36 -080085import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070086import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010087import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020088import com.android.systemui.statusbar.StatusBarState;
Selim Cinek515b2032017-11-15 10:20:19 -080089import com.android.systemui.statusbar.notification.AnimatableProperty;
Selim Cinek33223572016-02-19 19:32:22 -080090import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080091import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cinek515b2032017-11-15 10:20:19 -080092import com.android.systemui.statusbar.notification.PropertyAnimator;
Selim Cineka7d4f822016-12-06 14:34:47 -080093import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Selim Cinekb5605e52015-02-20 18:21:41 +010094import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050095import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinekaac93252015-04-14 20:04:12 -070096import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070097import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010098import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +010099
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700100import android.support.v4.graphics.ColorUtils;
101
Selim Cinek707e2072017-06-30 18:32:40 +0200102import java.io.FileDescriptor;
103import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200104import java.util.ArrayList;
Adrian Roos7d062c42017-03-30 15:11:43 -0700105import java.util.Collection;
Selim Cinek33223572016-02-19 19:32:22 -0800106import java.util.Collections;
107import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200108import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800109import java.util.List;
Selim Cinek572bbd42014-04-25 16:43:27 +0200110
Selim Cinek67b22602014-03-10 15:40:16 +0100111/**
112 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
113 */
114public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200115 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800116 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900117 NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
118 NotificationListContainer {
Selim Cinek67b22602014-03-10 15:40:16 +0100119
Selim Cinekd35c2792016-01-21 13:20:57 -0800120 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800121 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100122 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200123 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
124 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200125 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100126 /**
127 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
128 */
129 private static final int INVALID_POINTER = -1;
Selim Cinek515b2032017-11-15 10:20:19 -0800130 private static final AnimatableProperty SIDE_PADDINGS = AnimatableProperty.from(
131 "sidePaddings",
132 NotificationStackScrollLayout::setCurrentSidePadding,
133 NotificationStackScrollLayout::getCurrentSidePadding,
134 R.id.side_padding_animator_tag,
135 R.id.side_padding_animator_end_tag,
136 R.id.side_padding_animator_start_tag);
137 private static final AnimationProperties SIDE_PADDING_PROPERTIES =
138 new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
Selim Cinek67b22602014-03-10 15:40:16 +0100139
Selim Cinek1408eb52014-06-02 14:45:38 +0200140 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800141 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200142 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100143 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800144 private final Paint mBackgroundPaint = new Paint();
Selim Cinek515b2032017-11-15 10:20:19 -0800145 private final Path mBackgroundPath = new Path();
146 private final float[] mBackgroundRadii = new float[8];
Adrian Roosf0b4f962017-05-25 11:53:11 -0700147 private final boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100148
Selim Cinekbc243a92016-09-27 16:35:13 -0700149 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100150 private int mOwnScrollY;
151 private int mMaxLayoutHeight;
152
153 private VelocityTracker mVelocityTracker;
154 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100155 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100156 private int mTouchSlop;
157 private int mMinimumVelocity;
158 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100159 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200160 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100161 private boolean mIsBeingDragged;
162 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200163 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700164 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100165 private boolean mTouchIsClick;
166 private float mInitialTouchX;
167 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100168
Selim Cinek67b22602014-03-10 15:40:16 +0100169 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100170 private int mContentHeight;
171 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100172 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800173 private int mIncreasedPaddingBetweenElements;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200174 private int mTopPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700175 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700176 private int mBottomInset = 0;
Selim Cinek515b2032017-11-15 10:20:19 -0800177 private float mCurrentSidePadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100178
179 /**
180 * The algorithm which calculates the properties for our children
181 */
Muyuan Li87798022016-04-07 17:51:25 -0700182 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100183
184 /**
185 * The current State this Layout is in
186 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200187 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700188 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100189 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800190 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700191 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
192 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
193 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
194 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
195 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200196 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700197 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
198 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200199 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200200 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200201 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800202 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200203
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200204 /**
205 * The raw amount of the overScroll on the top, which is not rubber-banded.
206 */
207 private float mOverScrolledTopPixels;
208
209 /**
210 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
211 */
212 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900213 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200214 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200215 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100216 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200217 private boolean mNeedsAnimation;
218 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200219 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200220 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400221 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100222 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200223 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200224 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200225 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200226 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200227 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700228 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200229 private boolean mExpandingNotification;
230 private boolean mExpandedInThisMotion;
Muyuan Li84b45612016-06-01 11:05:08 -0700231 protected boolean mScrollingEnabled;
Muyuan Li8cf63dc2016-05-18 17:40:58 -0700232 protected DismissView mDismissView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700233 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400234 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700235 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200236
237 /**
238 * Was the scroller scrolled to the top when the down motion was observed?
239 */
240 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200241 /**
242 * The minimal amount of over scroll which is needed in order to switch to the quick settings
243 * when over scrolling on a expanded card.
244 */
245 private float mMinTopOverScrollToEscape;
246 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700247 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200248 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200249 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700250 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200251 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100252 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700253 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700254
Selim Cinek1408eb52014-06-02 14:45:38 +0200255 /**
256 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
257 * This is needed to avoid scrolling too far after the notification was collapsed in the same
258 * motion.
259 */
260 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000261 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800262
Mady Mellor95d743c2017-01-10 12:05:27 -0800263 private NotificationMenuRowPlugin mCurrMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800264 private View mTranslatingParentView;
Mady Mellor95d743c2017-01-10 12:05:27 -0800265 private View mMenuExposedView;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700266 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200267
268 /**
269 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
270 * animating.
271 */
272 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700273 private boolean mDisallowDismissInThisMotion;
Jorim Jaggi56306252014-07-03 00:40:09 +0200274 private boolean mInterceptDelegateEnabled;
275 private boolean mDelegateToScrollView;
Selim Cineka59ecc32015-04-07 10:51:49 -0700276 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700277 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200278 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200279 = new ViewTreeObserver.OnPreDrawListener() {
280 @Override
281 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400282 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200283 updateChildren();
284 mChildrenUpdateRequested = false;
285 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200286 return true;
287 }
288 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500289 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100290 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100291 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700292 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek0fccc722015-07-29 17:04:36 -0700293 private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700294 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
295 = new HashSet<>();
296 private HeadsUpManager mHeadsUpManager;
297 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700298 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200299 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700300 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700301 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800302 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700303 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800304 = new ViewTreeObserver.OnPreDrawListener() {
305 @Override
306 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700307 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800308 return true;
309 }
310 };
311 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800312 private Rect mStartAnimationRect = new Rect();
313 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800314 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800315 private boolean mAnimateNextBackgroundBottom;
316 private boolean mAnimateNextBackgroundTop;
317 private ObjectAnimator mBottomAnimator = null;
318 private ObjectAnimator mTopAnimator = null;
319 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
320 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800321 private int mBgColor;
322 private float mDimAmount;
323 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800324 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800325 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
326 @Override
327 public void onAnimationEnd(Animator animation) {
328 mDimAnimator = null;
329 }
330 };
331 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
332 = new ValueAnimator.AnimatorUpdateListener() {
333
334 @Override
335 public void onAnimationUpdate(ValueAnimator animation) {
336 setDimAmount((Float) animation.getAnimatedValue());
337 }
338 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700339 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800340 private boolean mContinuousShadowUpdate;
341 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
342 = new ViewTreeObserver.OnPreDrawListener() {
343
344 @Override
345 public boolean onPreDraw() {
346 updateViewShadows();
347 return true;
348 }
349 };
350 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
351 @Override
352 public int compare(ExpandableView view, ExpandableView otherView) {
353 float endY = view.getTranslationY() + view.getActualHeight();
354 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
355 if (endY < otherEndY) {
356 return -1;
357 } else if (endY > otherEndY) {
358 return 1;
359 } else {
360 // The two notifications end at the same location
361 return 0;
362 }
363 }
364 };
Selim Cinek25503252016-03-03 15:31:43 -0800365 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
Adrian Roos7d062c42017-03-30 15:11:43 -0700366 private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700367 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700368 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800369 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700370 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700371 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400372 private View mForcedScroll;
Selim Cinek972123d2016-05-03 14:25:58 -0700373 private float mBackgroundFadeAmount = 1.0f;
374 private static final Property<NotificationStackScrollLayout, Float> BACKGROUND_FADE =
375 new FloatProperty<NotificationStackScrollLayout>("backgroundFade") {
376 @Override
377 public void setValue(NotificationStackScrollLayout object, float value) {
378 object.setBackgroundFadeAmount(value);
379 }
380
381 @Override
382 public Float get(NotificationStackScrollLayout object) {
383 return object.getBackgroundFadeAmount();
384 }
385 };
Lucas Dupine17ce522017-07-17 15:45:06 -0700386 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700387 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700388 private boolean mForwardScrollable;
389 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700390 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700391 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800392 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700393 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800394 private boolean mNoAmbient;
395 private final Rect mClipRect = new Rect();
396 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700397 private Rect mRequestedClipBounds;
398 private boolean mInHeadsUpPinnedMode;
399 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800400 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100401 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700402 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800403 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek0fe07392017-11-09 13:26:34 -0800404 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800405 private int mLockscreenSidePaddings;
406 private int mSidePaddings;
Selim Cinek67b22602014-03-10 15:40:16 +0100407
408 public NotificationStackScrollLayout(Context context) {
409 this(context, null);
410 }
411
412 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
413 this(context, attrs, 0);
414 }
415
416 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
417 this(context, attrs, defStyleAttr, 0);
418 }
419
420 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
421 int defStyleRes) {
422 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800423 Resources res = getResources();
424
Selim Cinek281c2022016-10-13 19:14:43 -0700425 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800426 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800427 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
428 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200429 mExpandHelper = new ExpandHelper(getContext(), this,
430 minHeight, maxHeight);
431 mExpandHelper.setEventSource(this);
432 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800433 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Muyuan Li333a4fc2016-04-16 17:13:46 -0700434 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100435 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800436 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800437 mShouldDrawNotificationBackground =
438 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700439 mFadeNotificationsOnDismiss =
440 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800441
442 updateWillNotDraw();
Selim Cinek515b2032017-11-15 10:20:19 -0800443 mBackgroundPaint.setAntiAlias(true);
444 mBackgroundPaint.setStyle(Paint.Style.FILL);
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) {
Adrian Roosf0b4f962017-05-25 11:53:11 -0700491 if (mShouldDrawNotificationBackground && !mAmbientState.isDark()
492 && mCurrentBounds.top < mCurrentBounds.bottom) {
Selim Cinek515b2032017-11-15 10:20:19 -0800493 canvas.drawPath(mBackgroundPath, mBackgroundPaint);
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
Selim Cinekd35c2792016-01-21 13:20:57 -0800506 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800507 // No need to update the background color if it's not being drawn.
508 if (!mShouldDrawNotificationBackground) {
509 return;
510 }
511
Selim Cinekd35c2792016-01-21 13:20:57 -0800512 float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Selim Cinek972123d2016-05-03 14:25:58 -0700513 alpha *= mBackgroundFadeAmount;
Selim Cinekd35c2792016-01-21 13:20:57 -0800514 // We need to manually blend in the background color
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700515 int scrimColor = mScrimController.getBackgroundColor();
516 int color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100517 if (mCachedBackgroundColor != color) {
518 mCachedBackgroundColor = color;
519 mBackgroundPaint.setColor(color);
520 invalidate();
521 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800522 }
523
Selim Cinek67b22602014-03-10 15:40:16 +0100524 private void initView(Context context) {
525 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100526 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200527 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100528 final ViewConfiguration configuration = ViewConfiguration.get(context);
529 mTouchSlop = configuration.getScaledTouchSlop();
530 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
531 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100532 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700533
534 Resources res = context.getResources();
535 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800536 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700537 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700538 mPaddingBetweenElements = Math.max(1,
539 res.getDimensionPixelSize(R.dimen.notification_divider_height));
540 mIncreasedPaddingBetweenElements =
541 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
542 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200543 R.dimen.min_top_overscroll_to_qs);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700544 mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
545 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinek515b2032017-11-15 10:20:19 -0800546 mLockscreenSidePaddings = res.getDimensionPixelSize(
547 R.dimen.notification_lockscreen_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700548 mMinInteractionHeight = res.getDimensionPixelSize(
549 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800550 mCornerRadius = res.getDimensionPixelSize(
551 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cineka5eaa602014-05-12 21:27:47 +0200552 }
553
Selim Cinek25503252016-03-03 15:31:43 -0800554 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700555 mDrawBackgroundAsSrc = asSrc;
556 updateSrcDrawing();
557 }
558
559 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800560 if (!mShouldDrawNotificationBackground) {
561 return;
562 }
563
Selim Cinek48ff9b42016-11-09 19:31:51 -0800564 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700565 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800566 invalidate();
567 }
568
Selim Cinekaef92ef2014-06-06 18:06:04 +0200569 private void notifyHeightChangeListener(ExpandableView view) {
570 if (mOnHeightChangedListener != null) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100571 mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200572 }
Selim Cinek67b22602014-03-10 15:40:16 +0100573 }
574
575 @Override
576 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
577 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400578 // We need to measure all children even the GONE ones, such that the heights are calculated
579 // correctly as they are used to calculate how many we can fit on the screen.
580 final int size = getChildCount();
581 for (int i = 0; i < size; i++) {
Anthony Chen04d1ea72017-04-21 14:30:11 -0700582 measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400583 }
Selim Cinek67b22602014-03-10 15:40:16 +0100584 }
585
586 @Override
587 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100588 // we layout all our children centered on the top
589 float centerX = getWidth() / 2.0f;
590 for (int i = 0; i < getChildCount(); i++) {
591 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400592 // We need to layout all children even the GONE ones, such that the heights are
593 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100594 float width = child.getMeasuredWidth();
595 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100596 child.layout((int) (centerX - width / 2.0f),
597 0,
598 (int) (centerX + width / 2.0f),
599 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100600 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200601 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100602 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200603 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200604 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800605 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700606 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100607 }
608
Selim Cinek5bc852a2015-12-21 12:19:09 -0800609 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
610 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200611 mNeedViewResizeAnimation = true;
612 mNeedsAnimation = true;
613 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200614 }
615
Selim Cinekdb167372016-11-17 15:41:17 -0800616 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
617 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800618 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200619 }
620
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900621 @Override
622 public void setChildLocationsChangedListener(
623 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200624 mListener = listener;
625 }
626
Selim Cineka7d4f822016-12-06 14:34:47 -0800627 @Override
628 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
629 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200630 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800631 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200632 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100633 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800634 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100635 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800636 if (row.getVisibility() != View.VISIBLE) {
637 return false;
638 }
639 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200640 }
641
Selim Cinek67b22602014-03-10 15:40:16 +0100642 private void setMaxLayoutHeight(int maxLayoutHeight) {
643 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700644 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200645 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100646 }
647
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200648 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700649 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700650 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700651 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100652 }
653
Selim Cinekbc243a92016-09-27 16:35:13 -0700654 private void updateAlgorithmLayoutMinHeight() {
655 mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
656 }
657
Selim Cinek67b22602014-03-10 15:40:16 +0100658 /**
659 * Updates the children views according to the stack scroll algorithm. Call this whenever
660 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
661 */
662 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800663 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800664 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
665 ? 0
666 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200667 mAmbientState.setScrollY(mOwnScrollY);
668 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200669 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200670 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100671 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200672 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100673 }
674 }
675
Selim Cinekc383fd02016-10-21 15:31:26 -0700676 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700677 mShelf.updateAppearance();
Selim Cinekc383fd02016-10-21 15:31:26 -0700678 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
679 updateBackground();
680 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700681 }
682
Selim Cinek3776fe02016-02-04 13:32:43 -0800683 private void updateScrollStateForAddedChildren() {
684 if (mChildrenToAddAnimated.isEmpty()) {
685 return;
686 }
687 for (int i = 0; i < getChildCount(); i++) {
688 ExpandableView child = (ExpandableView) getChildAt(i);
689 if (mChildrenToAddAnimated.contains(child)) {
690 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800691 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
692 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
693 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800694 int childHeight = getIntrinsicHeight(child) + padding;
695 if (startingPosition < mOwnScrollY) {
696 // This child starts off screen, so let's keep it offscreen to keep the others visible
697
Selim Cinekef406062016-09-29 17:33:13 -0700698 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800699 }
700 }
701 }
702 clampScrollPosition();
703 }
704
Adrian Roos181385c2016-05-05 17:45:44 -0400705 private void updateForcedScroll() {
706 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
707 || !mForcedScroll.isAttachedToWindow())) {
708 mForcedScroll = null;
709 }
710 if (mForcedScroll != null) {
711 ExpandableView expandableView = (ExpandableView) mForcedScroll;
712 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
713 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700714 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400715
716 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700717
718 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
719 // that it is not visible anymore.
720 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700721 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400722 }
723 }
724 }
725
Selim Cinek319bdc42014-05-01 23:01:58 +0200726 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200727 if (!mChildrenUpdateRequested) {
728 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
729 mChildrenUpdateRequested = true;
730 invalidate();
731 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200732 }
733
Selim Cinek67b22602014-03-10 15:40:16 +0100734 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200735 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100736 }
737
Selim Cinekf7a14c02014-07-07 14:01:46 +0200738 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100739 int scrollRange = getScrollRange();
740 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700741 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100742 }
743 }
744
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200745 public int getTopPadding() {
746 return mTopPadding;
747 }
748
Selim Cinek1408eb52014-06-02 14:45:38 +0200749 private void setTopPadding(int topPadding, boolean animate) {
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200750 if (mTopPadding != topPadding) {
751 mTopPadding = topPadding;
752 updateAlgorithmHeightAndPadding();
753 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200754 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200755 mTopPaddingNeedsAnimation = true;
756 mNeedsAnimation = true;
757 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200758 requestChildrenUpdate();
Selim Cinekaef92ef2014-06-06 18:06:04 +0200759 notifyHeightChangeListener(null);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200760 }
761 }
762
763 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700764 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200765 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700766 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200767 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700768 public void setExpandedHeight(float height) {
769 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700770 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800771 int minExpansionHeight = getMinExpansionHeight();
772 if (height < minExpansionHeight) {
773 mClipRect.left = 0;
774 mClipRect.right = getWidth();
775 mClipRect.top = 0;
776 mClipRect.bottom = (int) height;
777 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700778 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800779 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700780 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800781 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200782 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700783 float translationY;
784 float appearEndPosition = getAppearEndPosition();
785 float appearStartPosition = getAppearStartPosition();
786 if (height >= appearEndPosition) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700787 translationY = 0;
Selim Cinek94c2d822016-07-13 18:50:04 -0700788 stackHeight = (int) height;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200789 } else {
Selim Cinek94c2d822016-07-13 18:50:04 -0700790 float appearFraction = getAppearFraction(height);
791 if (appearFraction >= 0) {
792 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
793 appearFraction);
794 } else {
795 // This may happen when pushing up a heads up. We linearly push it up from the
796 // start
797 translationY = height - appearStartPosition + getExpandTranslationStart();
798 }
799 stackHeight = (int) (height - translationY);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200800 }
801 if (stackHeight != mCurrentStackHeight) {
802 mCurrentStackHeight = stackHeight;
803 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200804 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200805 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700806 setStackTranslation(translationY);
Selim Cinekcafa87f2016-10-26 17:00:17 -0700807 }
808
809 private void setRequestedClipBounds(Rect clipRect) {
810 mRequestedClipBounds = clipRect;
811 updateClipping();
812 }
813
814 public void updateClipping() {
815 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
816 && !mHeadsUpAnimatingAway;
817 if (mIsClipped != clipped) {
818 mIsClipped = clipped;
819 updateFadingState();
820 }
821 if (clipped) {
822 setClipBounds(mRequestedClipBounds);
823 } else {
824 setClipBounds(null);
825 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700826 }
827
828 /**
829 * @return The translation at the beginning when expanding.
830 * Measured relative to the resting position.
831 */
832 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800833 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700834 }
835
836 /**
837 * @return the position from where the appear transition starts when expanding.
838 * Measured in absolute height.
839 */
840 private float getAppearStartPosition() {
Selim Cinekd127d792016-11-01 19:11:41 -0700841 if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
Selim Cinekebf42342017-07-13 15:46:10 +0200842 if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) {
Selim Cinekd127d792016-11-01 19:11:41 -0700843 // If we ever expanded beyond the first notification, it's allowed to merge into
844 // the shelf
845 return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
846 }
847 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800848 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700849 }
850
851 /**
852 * @return the position from where the appear transition ends when expanding.
853 * Measured in absolute height.
854 */
855 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -0700856 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -0700857 int notGoneChildCount = getNotGoneChildCount();
858 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100859 int minNotificationsForShelf = 1;
Selim Cinekebf42342017-07-13 15:46:10 +0200860 if (mTrackingHeadsUp
861 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100862 appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
863 minNotificationsForShelf = 2;
864 } else {
865 appearPosition = 0;
866 }
Selim Cinek66440cf2017-05-26 13:48:47 -0700867 if (notGoneChildCount >= minNotificationsForShelf
Anthony Chen9e05d462017-04-07 10:10:21 -0700868 && mShelf.getVisibility() != GONE) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100869 appearPosition += mShelf.getIntrinsicHeight();
870 }
Selim Cinekaa417da2016-10-27 18:17:08 -0700871 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +0100872 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -0700873 }
874 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -0700875 }
876
877 /**
878 * @param height the height of the panel
879 * @return the fraction of the appear animation that has been performed
880 */
881 public float getAppearFraction(float height) {
882 float appearEndPosition = getAppearEndPosition();
883 float appearStartPosition = getAppearStartPosition();
884 return (height - appearStartPosition)
885 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700886 }
887
Selim Cinekd2281152015-04-10 14:37:46 -0700888 public float getStackTranslation() {
889 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700890 }
891
Selim Cinekd2281152015-04-10 14:37:46 -0700892 private void setStackTranslation(float stackTranslation) {
893 if (stackTranslation != mStackTranslation) {
894 mStackTranslation = stackTranslation;
895 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700896 requestChildrenUpdate();
897 }
Selim Cinek67b22602014-03-10 15:40:16 +0100898 }
899
900 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100901 * 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 +0100902 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
903 *
904 * @return either the layout height or the externally defined height, whichever is smaller
905 */
Selim Cinek343e6e22014-04-11 21:23:30 +0200906 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +0100907 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
908 }
909
Selim Cinek816c8e42015-11-19 12:00:45 -0800910 public int getFirstItemMinHeight() {
911 final ExpandableView firstChild = getFirstChildNotGone();
912 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100913 }
914
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000915 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
Dan Sandler4247a5c2014-07-23 15:58:08 -0400916 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +0100917 }
918
Jason Monk16ac3772016-02-10 15:39:21 -0500919 public void setQsContainer(ViewGroup qsContainer) {
920 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +0200921 }
922
Mady Mellorb0a82462016-04-30 17:31:02 -0700923 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100924 public void onChildDismissed(View v) {
Selim Cinekd1395642016-04-28 12:22:42 -0700925 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
926 if (!row.isDismissed()) {
927 handleChildDismissed(v);
928 }
929 ViewGroup transientContainer = row.getTransientContainer();
930 if (transientContainer != null) {
931 transientContainer.removeTransientView(v);
932 }
933 }
934
935 private void handleChildDismissed(View v) {
Dan Sandlereceda3d2014-07-21 15:35:01 -0400936 if (mDismissAllInProgress) {
937 return;
938 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100939 setSwipingInProgress(false);
Selim Cinekeb973562014-05-02 17:07:49 +0200940 if (mDragAnimPendingChildren.contains(v)) {
941 // We start the swipe and finish it in the same frame, we don't want any animation
942 // for the drag
943 mDragAnimPendingChildren.remove(v);
944 }
Selim Cinek572bbd42014-04-25 16:43:27 +0200945 mSwipedOutViews.add(v);
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200946 mAmbientState.onDragFinished(v);
Selim Cinek33223572016-02-19 19:32:22 -0800947 updateContinuousShadowDrawing();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700948 if (v instanceof ExpandableNotificationRow) {
949 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
950 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -0700951 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700952 }
953 }
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +0900954 if (v instanceof ExpandableNotificationRow) {
955 ((ExpandableNotificationRow) v).performDismiss(false /* fromAccessibility */);
956 }
Blazej Magnowski72323322015-07-24 11:49:40 -0700957
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700958 mFalsingManager.onNotificationDismissed();
959 if (mFalsingManager.shouldEnforceBouncer()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500960 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
Felipe Lemeee5d6302016-04-22 16:11:19 -0700961 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -0700962 }
Selim Cinekeb973562014-05-02 17:07:49 +0200963 }
964
965 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -0800966 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200967 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -0800968 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +0200969 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +0200970 if (mAnimationsEnabled) {
971 mSnappedBackChildren.add(animView);
972 mNeedsAnimation = true;
973 }
Selim Cinekeb973562014-05-02 17:07:49 +0200974 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +0200975 } else {
976 // We start the swipe and snap back in the same frame, we don't want any animation
977 mDragAnimPendingChildren.remove(animView);
978 }
Mady Mellor95d743c2017-01-10 12:05:27 -0800979 if (mCurrMenuRow != null && targetLeft == 0) {
980 mCurrMenuRow.resetMenu();
981 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -0800982 }
Selim Cinek67b22602014-03-10 15:40:16 +0100983 }
984
Adrian Roos5d9cc662014-05-28 17:08:13 +0200985 @Override
986 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Selim Cinek131c1e22015-05-11 19:04:49 -0700987 if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
Selim Cinekaac93252015-04-14 20:04:12 -0700988 mScrimController.setTopHeadsUpDragAmount(animView,
Winsonbde852d2016-04-15 19:06:54 -0700989 Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
Selim Cinekaac93252015-04-14 20:04:12 -0700990 }
Anthony Chen7acbb772017-04-07 16:45:25 -0700991 // Returning true prevents alpha fading.
992 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +0200993 }
994
Mady Mellorb0a82462016-04-30 17:31:02 -0700995 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100996 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700997 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100998 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200999 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -08001000 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -07001001 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001002 mDragAnimPendingChildren.add(v);
1003 mNeedsAnimation = true;
1004 }
Selim Cinekeb973562014-05-02 17:07:49 +02001005 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +01001006 }
1007
Selim Cinek684a4422015-04-15 16:18:39 -07001008 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001009 if (v instanceof ExpandableNotificationRow) {
1010 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001011 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001012 }
1013 return false;
1014 }
1015
1016 private boolean isHeadsUp(View v) {
1017 if (v instanceof ExpandableNotificationRow) {
1018 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1019 return row.isHeadsUp();
1020 }
1021 return false;
1022 }
1023
Mady Mellorb0a82462016-04-30 17:31:02 -07001024 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001025 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001026 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001027 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001028 }
1029
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001030 @Override
1031 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001032 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001033 }
1034
Mady Mellor4b80b102016-01-22 08:03:58 -08001035 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001036 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001037 View child = getChildAtPosition(ev.getX(), ev.getY());
1038 if (child instanceof ExpandableNotificationRow) {
1039 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1040 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001041 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001042 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001043 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001044 || (parent.getNotificationChildren().size() == 1
1045 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001046 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001047 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001048 // child notifications so we use the parent of the child. We also do the same
1049 // if we only have a single child.
1050 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001051 }
1052 }
1053 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001054 }
1055
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001056 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1057 getLocationOnScreen(mTempInt2);
1058 float localTouchY = touchY - mTempInt2[1];
1059
1060 ExpandableView closestChild = null;
1061 float minDist = Float.MAX_VALUE;
1062
1063 // find the view closest to the location, accounting for GONE views
1064 final int count = getChildCount();
1065 for (int childIdx = 0; childIdx < count; childIdx++) {
1066 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1067 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001068 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001069 continue;
1070 }
1071 float childTop = slidingChild.getTranslationY();
1072 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001073 float bottom = childTop + slidingChild.getActualHeight()
1074 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001075
1076 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1077 if (dist < minDist) {
1078 closestChild = slidingChild;
1079 minDist = dist;
1080 }
1081 }
1082 return closestChild;
1083 }
1084
Mady Mellorb0a82462016-04-30 17:31:02 -07001085 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001086 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001087 getLocationOnScreen(mTempInt2);
1088 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001089 }
1090
Mady Mellorb0a82462016-04-30 17:31:02 -07001091 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001092 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001093 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1094
1095 }
1096
1097 /**
1098 * Get the child at a certain screen location.
1099 *
1100 * @param touchX the x coordinate
1101 * @param touchY the y coordinate
1102 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1103 * @return the child at the given location.
1104 */
1105 private ExpandableView getChildAtPosition(float touchX, float touchY,
1106 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001107 // find the view under the pointer, accounting for GONE views
1108 final int count = getChildCount();
1109 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001110 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001111 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001112 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001113 continue;
1114 }
Selim Cinek89faff12014-06-19 16:29:04 -07001115 float childTop = slidingChild.getTranslationY();
1116 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001117 float bottom = childTop + slidingChild.getActualHeight()
1118 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001119
1120 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1121 // camera affordance).
1122 int left = 0;
1123 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001124
Selim Cinek34ed7c02017-09-08 15:03:12 -07001125 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001126 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001127 if (slidingChild instanceof ExpandableNotificationRow) {
1128 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001129 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Selim Cinek5bc852a2015-12-21 12:19:09 -08001130 && mHeadsUpManager.getTopEntry().entry.row != row
1131 && mGroupManager.getGroupSummary(
1132 mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
1133 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001134 continue;
1135 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001136 return row.getViewAtPosition(touchY - childTop);
1137 }
Selim Cinek67b22602014-03-10 15:40:16 +01001138 return slidingChild;
1139 }
1140 }
1141 return null;
1142 }
1143
Mady Mellorb0a82462016-04-30 17:31:02 -07001144 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001145 public boolean canChildBeExpanded(View v) {
1146 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001147 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001148 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001149 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001150 }
1151
Chris Wren6abeeb92016-05-26 14:44:38 -04001152 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001153 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001154 public void setUserExpandedChild(View v, boolean userExpanded) {
1155 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001156 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001157 if (userExpanded && onKeyguard()) {
1158 // Due to a race when locking the screen while touching, a notification may be
1159 // expanded even after we went back to keyguard. An example of this happens if
1160 // you click in the empty space while expanding a group.
1161
1162 // We also need to un-user lock it here, since otherwise the content height
1163 // calculated might be wrong. We also can't invert the two calls since
1164 // un-userlocking it will trigger a layout switch in the content view.
1165 row.setUserLocked(false);
1166 updateContentHeight();
1167 notifyHeightChangeListener(row);
1168 return;
1169 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001170 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1171 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001172 }
1173 }
1174
Mady Mellorb0a82462016-04-30 17:31:02 -07001175 @Override
1176 public void setExpansionCancelled(View v) {
1177 if (v instanceof ExpandableNotificationRow) {
1178 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1179 }
1180 }
1181
1182 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001183 public void setUserLockedChild(View v, boolean userLocked) {
1184 if (v instanceof ExpandableNotificationRow) {
1185 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1186 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001187 cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02001188 requestDisallowInterceptTouchEvent(true);
1189 }
1190
1191 @Override
1192 public void expansionStateChanged(boolean isExpanding) {
1193 mExpandingNotification = isExpanding;
1194 if (!mExpandedInThisMotion) {
1195 mMaxScrollAfterExpand = mOwnScrollY;
1196 mExpandedInThisMotion = true;
1197 }
1198 }
1199
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001200 @Override
1201 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001202 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001203 }
1204
Selim Cinek1408eb52014-06-02 14:45:38 +02001205 public void setScrollingEnabled(boolean enable) {
1206 mScrollingEnabled = enable;
1207 }
1208
Adrian Roos181385c2016-05-05 17:45:44 -04001209 public void lockScrollTo(View v) {
1210 if (mForcedScroll == v) {
1211 return;
1212 }
1213 mForcedScroll = v;
1214 scrollTo(v);
1215 }
1216
Ricky Waicd35def2016-05-03 11:07:07 +01001217 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001218 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001219 int positionInLinearLayout = getPositionInLinearLayout(v);
1220 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1221 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001222
Adrian Roos4a579672016-05-24 16:54:37 -07001223 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1224 // that it is not visible anymore.
1225 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001226 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1227 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001228 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001229 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001230 }
Ricky Waicd35def2016-05-03 11:07:07 +01001231 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001232 }
1233
Adrian Roos181385c2016-05-05 17:45:44 -04001234 /**
1235 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1236 * the IME.
1237 */
1238 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1239 return positionInLinearLayout + v.getIntrinsicHeight() +
1240 getImeInset() - getHeight() + getTopPadding();
1241 }
1242
Adrian Roos5153d4a2016-03-22 10:01:56 -07001243 @Override
1244 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001245 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001246
1247 int range = getScrollRange();
1248 if (mOwnScrollY > range) {
1249 // HACK: We're repeatedly getting staggered insets here while the IME is
1250 // animating away. To work around that we'll wait until things have settled.
1251 removeCallbacks(mReclamp);
1252 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001253 } else if (mForcedScroll != null) {
1254 // The scroll was requested before we got the actual inset - in case we need
1255 // to scroll up some more do so now.
1256 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001257 }
1258 return insets;
1259 }
1260
1261 private Runnable mReclamp = new Runnable() {
1262 @Override
1263 public void run() {
1264 int range = getScrollRange();
1265 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1266 mDontReportNextOverScroll = true;
1267 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001268 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001269 }
1270 };
1271
Selim Cinek1408eb52014-06-02 14:45:38 +02001272 public void setExpandingEnabled(boolean enable) {
1273 mExpandHelper.setEnabled(enable);
1274 }
1275
1276 private boolean isScrollingEnabled() {
1277 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001278 }
1279
Mady Mellorb0a82462016-04-30 17:31:02 -07001280 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001281 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001282 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001283 }
1284
Selim Cinek19c8c702014-08-25 22:09:19 +02001285 @Override
1286 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001287 return onKeyguard();
1288 }
1289
1290 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001291 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001292 }
1293
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001294 private void setSwipingInProgress(boolean isSwiped) {
1295 mSwipingInProgress = isSwiped;
1296 if(isSwiped) {
1297 requestDisallowInterceptTouchEvent(true);
1298 }
Selim Cinek67b22602014-03-10 15:40:16 +01001299 }
1300
1301 @Override
1302 protected void onConfigurationChanged(Configuration newConfig) {
1303 super.onConfigurationChanged(newConfig);
1304 float densityScale = getResources().getDisplayMetrics().density;
1305 mSwipeHelper.setDensityScale(densityScale);
1306 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1307 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1308 initView(getContext());
1309 }
1310
Dan Sandlereceda3d2014-07-21 15:35:01 -04001311 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001312 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1313 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001314 }
1315
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001316 @Override
Mady Mellor86889c22016-04-18 16:37:06 -07001317 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001318 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001319 // If the child is showing the notification menu snap to that
1320 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001321 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001322 }
1323
Selim Cinek67b22602014-03-10 15:40:16 +01001324 @Override
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001325 public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
1326 return this;
1327 }
1328
1329 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001330 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001331 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1332 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001333 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001334 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001335 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001336 if (isCancelOrUp) {
1337 mExpandHelper.onlyObserveMovements(false);
1338 }
1339 boolean wasExpandingBefore = mExpandingNotification;
1340 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001341 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1342 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001343 dispatchDownEventToScroller(ev);
1344 }
1345 }
Selim Cinek67b22602014-03-10 15:40:16 +01001346 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001347 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1348 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001349 scrollerWantsIt = onScrollTouch(ev);
1350 }
1351 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001352 if (!mIsBeingDragged
1353 && !mExpandingNotification
1354 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001355 && !mOnlyScrollingInThisMotion
1356 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001357 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1358 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001359
1360 // Check if we need to clear any snooze leavebehinds
Eliot Courtney47098cb2017-10-18 17:30:30 +09001361 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07001362 if (guts != null && !isTouchInView(ev, guts)
1363 && guts.getGutsContent() instanceof NotificationSnooze) {
1364 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1365 if ((ns.isExpanded() && isCancelOrUp)
1366 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1367 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1368 // clear it on the next non-horizontal swipe or expand event.
1369 checkSnoozeLeavebehind();
1370 }
1371 }
1372 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1373 mCheckForLeavebehind = true;
1374 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001375 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1376 }
1377
1378 private void dispatchDownEventToScroller(MotionEvent ev) {
1379 MotionEvent downEvent = MotionEvent.obtain(ev);
1380 downEvent.setAction(MotionEvent.ACTION_DOWN);
1381 onScrollTouch(downEvent);
1382 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001383 }
1384
Selim Cinek11e33232016-08-05 15:30:53 -07001385 @Override
1386 public boolean onGenericMotionEvent(MotionEvent event) {
1387 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1388 || mDisallowScrollingInThisMotion) {
1389 return false;
1390 }
1391 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1392 switch (event.getAction()) {
1393 case MotionEvent.ACTION_SCROLL: {
1394 if (!mIsBeingDragged) {
1395 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1396 if (vscroll != 0) {
1397 final int delta = (int) (vscroll * getVerticalScrollFactor());
1398 final int range = getScrollRange();
1399 int oldScrollY = mOwnScrollY;
1400 int newScrollY = oldScrollY - delta;
1401 if (newScrollY < 0) {
1402 newScrollY = 0;
1403 } else if (newScrollY > range) {
1404 newScrollY = range;
1405 }
1406 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001407 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001408 return true;
1409 }
1410 }
1411 }
1412 }
1413 }
1414 }
1415 return super.onGenericMotionEvent(event);
1416 }
1417
Selim Cinek67b22602014-03-10 15:40:16 +01001418 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001419 if (!isScrollingEnabled()) {
1420 return false;
1421 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001422 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001423 return false;
1424 }
Adrian Roos181385c2016-05-05 17:45:44 -04001425 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001426 initVelocityTrackerIfNotExists();
1427 mVelocityTracker.addMovement(ev);
1428
1429 final int action = ev.getAction();
1430
1431 switch (action & MotionEvent.ACTION_MASK) {
1432 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001433 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001434 return false;
1435 }
1436 boolean isBeingDragged = !mScroller.isFinished();
1437 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001438 /*
1439 * If being flinged and user touches, stop the fling. isFinished
1440 * will be false if being flinged.
1441 */
1442 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001443 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001444 }
1445
1446 // Remember where the motion event started
1447 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001448 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001449 mActivePointerId = ev.getPointerId(0);
1450 break;
1451 }
1452 case MotionEvent.ACTION_MOVE:
1453 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1454 if (activePointerIndex == -1) {
1455 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1456 break;
1457 }
1458
1459 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001460 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001461 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001462 final int xDiff = Math.abs(x - mDownX);
1463 final int yDiff = Math.abs(deltaY);
1464 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001465 setIsBeingDragged(true);
1466 if (deltaY > 0) {
1467 deltaY -= mTouchSlop;
1468 } else {
1469 deltaY += mTouchSlop;
1470 }
1471 }
1472 if (mIsBeingDragged) {
1473 // Scroll to follow the motion event
1474 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001475 int range = getScrollRange();
1476 if (mExpandedInThisMotion) {
1477 range = Math.min(range, mMaxScrollAfterExpand);
1478 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001479
1480 float scrollAmount;
1481 if (deltaY < 0) {
1482 scrollAmount = overScrollDown(deltaY);
1483 } else {
1484 scrollAmount = overScrollUp(deltaY, range);
1485 }
Selim Cinek67b22602014-03-10 15:40:16 +01001486
Selim Cinek9212de82017-02-06 16:04:28 -08001487 // Calling customOverScrollBy will call onCustomOverScrolled, which
1488 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001489 if (scrollAmount != 0.0f) {
1490 // The scrolling motion could not be compensated with the
1491 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001492 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1493 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001494 // If we're scrolling, leavebehinds should be dismissed
1495 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001496 }
Selim Cinek67b22602014-03-10 15:40:16 +01001497 }
1498 break;
1499 case MotionEvent.ACTION_UP:
1500 if (mIsBeingDragged) {
1501 final VelocityTracker velocityTracker = mVelocityTracker;
1502 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1503 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1504
Selim Cinek1408eb52014-06-02 14:45:38 +02001505 if (shouldOverScrollFling(initialVelocity)) {
1506 onOverScrollFling(true, initialVelocity);
1507 } else {
1508 if (getChildCount() > 0) {
1509 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1510 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1511 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1512 fling(-initialVelocity);
1513 } else {
1514 onOverScrollFling(false, initialVelocity);
1515 }
1516 } else {
1517 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1518 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001519 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001520 }
Selim Cinek67b22602014-03-10 15:40:16 +01001521 }
1522 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001523 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001524 mActivePointerId = INVALID_POINTER;
1525 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001526 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001527
Selim Cinek67b22602014-03-10 15:40:16 +01001528 break;
1529 case MotionEvent.ACTION_CANCEL:
1530 if (mIsBeingDragged && getChildCount() > 0) {
1531 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001532 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001533 }
1534 mActivePointerId = INVALID_POINTER;
1535 endDrag();
1536 }
1537 break;
1538 case MotionEvent.ACTION_POINTER_DOWN: {
1539 final int index = ev.getActionIndex();
1540 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001541 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001542 mActivePointerId = ev.getPointerId(index);
1543 break;
1544 }
1545 case MotionEvent.ACTION_POINTER_UP:
1546 onSecondaryPointerUp(ev);
1547 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001548 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001549 break;
1550 }
1551 return true;
1552 }
1553
Muyuan Li84b45612016-06-01 11:05:08 -07001554 protected boolean isInsideQsContainer(MotionEvent ev) {
1555 return ev.getY() < mQsContainer.getBottom();
1556 }
1557
Selim Cinek1408eb52014-06-02 14:45:38 +02001558 private void onOverScrollFling(boolean open, int initialVelocity) {
1559 if (mOverscrollTopChangedListener != null) {
1560 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1561 }
1562 mDontReportNextOverScroll = true;
1563 setOverScrollAmount(0.0f, true, false);
1564 }
1565
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001566 /**
1567 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1568 *
1569 * @param deltaY The amount to scroll upwards, has to be positive.
1570 * @return The amount of scrolling to be performed by the scroller,
1571 * not handled by the overScroll amount.
1572 */
1573 private float overScrollUp(int deltaY, int range) {
1574 deltaY = Math.max(deltaY, 0);
1575 float currentTopAmount = getCurrentOverScrollAmount(true);
1576 float newTopAmount = currentTopAmount - deltaY;
1577 if (currentTopAmount > 0) {
1578 setOverScrollAmount(newTopAmount, true /* onTop */,
1579 false /* animate */);
1580 }
1581 // Top overScroll might not grab all scrolling motion,
1582 // we have to scroll as well.
1583 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1584 float newScrollY = mOwnScrollY + scrollAmount;
1585 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001586 if (!mExpandedInThisMotion) {
1587 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1588 // We overScroll on the top
1589 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1590 false /* onTop */,
1591 false /* animate */);
1592 }
Selim Cinekef406062016-09-29 17:33:13 -07001593 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001594 scrollAmount = 0.0f;
1595 }
1596 return scrollAmount;
1597 }
1598
1599 /**
1600 * Perform a scroll downward and adapt the overscroll amounts accordingly
1601 *
1602 * @param deltaY The amount to scroll downwards, has to be negative.
1603 * @return The amount of scrolling to be performed by the scroller,
1604 * not handled by the overScroll amount.
1605 */
1606 private float overScrollDown(int deltaY) {
1607 deltaY = Math.min(deltaY, 0);
1608 float currentBottomAmount = getCurrentOverScrollAmount(false);
1609 float newBottomAmount = currentBottomAmount + deltaY;
1610 if (currentBottomAmount > 0) {
1611 setOverScrollAmount(newBottomAmount, false /* onTop */,
1612 false /* animate */);
1613 }
1614 // Bottom overScroll might not grab all scrolling motion,
1615 // we have to scroll as well.
1616 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1617 float newScrollY = mOwnScrollY + scrollAmount;
1618 if (newScrollY < 0) {
1619 float currentTopPixels = getCurrentOverScrolledPixels(true);
1620 // We overScroll on the top
1621 setOverScrolledPixels(currentTopPixels - newScrollY,
1622 true /* onTop */,
1623 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001624 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001625 scrollAmount = 0.0f;
1626 }
1627 return scrollAmount;
1628 }
1629
Selim Cinek67b22602014-03-10 15:40:16 +01001630 private void onSecondaryPointerUp(MotionEvent ev) {
1631 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1632 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1633 final int pointerId = ev.getPointerId(pointerIndex);
1634 if (pointerId == mActivePointerId) {
1635 // This was our active pointer going up. Choose a new
1636 // active pointer and adjust accordingly.
1637 // TODO: Make this decision more intelligent.
1638 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1639 mLastMotionY = (int) ev.getY(newPointerIndex);
1640 mActivePointerId = ev.getPointerId(newPointerIndex);
1641 if (mVelocityTracker != null) {
1642 mVelocityTracker.clear();
1643 }
1644 }
1645 }
1646
1647 private void initVelocityTrackerIfNotExists() {
1648 if (mVelocityTracker == null) {
1649 mVelocityTracker = VelocityTracker.obtain();
1650 }
1651 }
1652
1653 private void recycleVelocityTracker() {
1654 if (mVelocityTracker != null) {
1655 mVelocityTracker.recycle();
1656 mVelocityTracker = null;
1657 }
1658 }
1659
1660 private void initOrResetVelocityTracker() {
1661 if (mVelocityTracker == null) {
1662 mVelocityTracker = VelocityTracker.obtain();
1663 } else {
1664 mVelocityTracker.clear();
1665 }
1666 }
1667
Ricky Waicd35def2016-05-03 11:07:07 +01001668 public void setFinishScrollingCallback(Runnable runnable) {
1669 mFinishScrollingCallback = runnable;
1670 }
1671
Selim Cinek9212de82017-02-06 16:04:28 -08001672 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001673 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001674 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001675 int y = mScroller.getCurrY();
1676
Selim Cinek9212de82017-02-06 16:04:28 -08001677 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001678 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001679 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1680 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001681 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001682 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1683 }
1684 }
Selim Cinek67b22602014-03-10 15:40:16 +01001685
Adrian Roos5153d4a2016-03-22 10:01:56 -07001686 if (mDontClampNextScroll) {
1687 range = Math.max(range, oldY);
1688 }
Selim Cinek9212de82017-02-06 16:04:28 -08001689 customOverScrollBy(y - oldY, oldY, range,
1690 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001691 }
1692
Selim Cinek9212de82017-02-06 16:04:28 -08001693 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001694 } else {
1695 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001696 if (mFinishScrollingCallback != null) {
1697 mFinishScrollingCallback.run();
1698 }
Selim Cinek67b22602014-03-10 15:40:16 +01001699 }
1700 }
1701
Selim Cinek9212de82017-02-06 16:04:28 -08001702 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1703 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001704
1705 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001706 final int top = -maxOverScrollY;
1707 final int bottom = maxOverScrollY + scrollRangeY;
1708
1709 boolean clampedY = false;
1710 if (newScrollY > bottom) {
1711 newScrollY = bottom;
1712 clampedY = true;
1713 } else if (newScrollY < top) {
1714 newScrollY = top;
1715 clampedY = true;
1716 }
1717
Selim Cinek9212de82017-02-06 16:04:28 -08001718 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001719
1720 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001721 }
1722
1723 /**
1724 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1725 * overscroll effect based on numPixels. By default this will also cancel animations on the
1726 * same overScroll edge.
1727 *
1728 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1729 * the rubber-banding logic.
1730 * @param onTop Should the effect be applied on top of the scroller.
1731 * @param animate Should an animation be performed.
1732 */
1733 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001734 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001735 }
1736
1737 /**
1738 * Set the effective overScroll amount which will be directly reflected in the layout.
1739 * By default this will also cancel animations on the same overScroll edge.
1740 *
1741 * @param amount The amount to overScroll by.
1742 * @param onTop Should the effect be applied on top of the scroller.
1743 * @param animate Should an animation be performed.
1744 */
1745 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1746 setOverScrollAmount(amount, onTop, animate, true);
1747 }
1748
1749 /**
1750 * Set the effective overScroll amount which will be directly reflected in the layout.
1751 *
1752 * @param amount The amount to overScroll by.
1753 * @param onTop Should the effect be applied on top of the scroller.
1754 * @param animate Should an animation be performed.
1755 * @param cancelAnimators Should running animations be cancelled.
1756 */
1757 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1758 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001759 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1760 }
1761
1762 /**
1763 * Set the effective overScroll amount which will be directly reflected in the layout.
1764 *
1765 * @param amount The amount to overScroll by.
1766 * @param onTop Should the effect be applied on top of the scroller.
1767 * @param animate Should an animation be performed.
1768 * @param cancelAnimators Should running animations be cancelled.
1769 * @param isRubberbanded The value which will be passed to
1770 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1771 */
1772 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1773 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001774 if (cancelAnimators) {
1775 mStateAnimator.cancelOverScrollAnimators(onTop);
1776 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001777 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001778 }
1779
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001780 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1781 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001782 amount = Math.max(0, amount);
1783 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001784 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001785 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001786 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001787 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001788 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001789 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001790 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001791 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001792 }
1793 }
1794
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001795 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001796 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1797 if (mDontReportNextOverScroll) {
1798 mDontReportNextOverScroll = false;
1799 return;
1800 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001801 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001802 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001803 }
1804 }
1805
1806 public void setOverscrollTopChangedListener(
1807 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1808 mOverscrollTopChangedListener = overscrollTopChangedListener;
1809 }
1810
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001811 public float getCurrentOverScrollAmount(boolean top) {
1812 return mAmbientState.getOverScrollAmount(top);
1813 }
1814
1815 public float getCurrentOverScrolledPixels(boolean top) {
1816 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
1817 }
1818
1819 private void setOverScrolledPixels(float amount, boolean onTop) {
1820 if (onTop) {
1821 mOverScrolledTopPixels = amount;
1822 } else {
1823 mOverScrolledBottomPixels = amount;
1824 }
1825 }
1826
Selim Cinek9212de82017-02-06 16:04:28 -08001827 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001828 // Treat animating scrolls differently; see #computeScroll() for why.
1829 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07001830 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001831 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001832 springBack();
1833 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02001834 float overScrollTop = getCurrentOverScrollAmount(true);
1835 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001836 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001837 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001838 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001839 }
Selim Cinek67b22602014-03-10 15:40:16 +01001840 }
Selim Cinek67b22602014-03-10 15:40:16 +01001841 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08001842 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001843 }
1844 }
1845
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001846 private void springBack() {
1847 int scrollRange = getScrollRange();
1848 boolean overScrolledTop = mOwnScrollY <= 0;
1849 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
1850 if (overScrolledTop || overScrolledBottom) {
1851 boolean onTop;
1852 float newAmount;
1853 if (overScrolledTop) {
1854 onTop = true;
1855 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07001856 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02001857 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001858 } else {
1859 onTop = false;
1860 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07001861 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001862 }
1863 setOverScrollAmount(newAmount, onTop, false);
1864 setOverScrollAmount(0.0f, onTop, true);
1865 mScroller.forceFinished(true);
1866 }
1867 }
1868
Selim Cinek67b22602014-03-10 15:40:16 +01001869 private int getScrollRange() {
Selim Cineka424c502016-04-05 13:07:54 -07001870 int contentHeight = getContentHeight();
Selim Cinekdb167372016-11-17 15:41:17 -08001871 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07001872 int imeInset = getImeInset();
1873 scrollRange += Math.min(imeInset, Math.max(0,
1874 getContentHeight() - (getHeight() - imeInset)));
1875 return scrollRange;
1876 }
1877
1878 private int getImeInset() {
1879 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01001880 }
1881
Selim Cinek343e6e22014-04-11 21:23:30 +02001882 /**
1883 * @return the first child which has visibility unequal to GONE
1884 */
Selim Cinekb55386d2015-12-16 17:26:49 -08001885 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02001886 int childCount = getChildCount();
1887 for (int i = 0; i < childCount; i++) {
1888 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001889 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08001890 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02001891 }
1892 }
1893 return null;
1894 }
1895
Selim Cinek4a1ac842014-05-01 15:51:58 +02001896 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001897 * @return the child before the given view which has visibility unequal to GONE
1898 */
1899 public ExpandableView getViewBeforeView(ExpandableView view) {
1900 ExpandableView previousView = null;
1901 int childCount = getChildCount();
1902 for (int i = 0; i < childCount; i++) {
1903 View child = getChildAt(i);
1904 if (child == view) {
1905 return previousView;
1906 }
1907 if (child.getVisibility() != View.GONE) {
1908 previousView = (ExpandableView) child;
1909 }
1910 }
1911 return null;
1912 }
1913
1914 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001915 * @return The first child which has visibility unequal to GONE which is currently below the
1916 * given translationY or equal to it.
1917 */
Selim Cinekef8c2252017-02-10 14:52:18 -08001918 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001919 int childCount = getChildCount();
1920 for (int i = 0; i < childCount; i++) {
1921 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08001922 if (child.getVisibility() == View.GONE) {
1923 continue;
1924 }
1925 float rowTranslation = child.getTranslationY();
1926 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001927 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08001928 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
1929 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1930 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
1931 List<ExpandableNotificationRow> notificationChildren =
1932 row.getNotificationChildren();
1933 for (int childIndex = 0; childIndex < notificationChildren.size();
1934 childIndex++) {
1935 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
1936 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
1937 return rowChild;
1938 }
1939 }
1940 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001941 }
1942 }
1943 return null;
1944 }
1945
1946 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02001947 * @return the last child which has visibility unequal to GONE
1948 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001949 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02001950 int childCount = getChildCount();
1951 for (int i = childCount - 1; i >= 0; i--) {
1952 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001953 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02001954 return child;
1955 }
1956 }
1957 return null;
1958 }
1959
Jorim Jaggi069cd032014-05-15 03:09:01 +02001960 /**
1961 * @return the number of children which have visibility unequal to GONE
1962 */
1963 public int getNotGoneChildCount() {
1964 int childCount = getChildCount();
1965 int count = 0;
1966 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07001967 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001968 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02001969 count++;
1970 }
1971 }
1972 return count;
1973 }
1974
Jorim Jaggi30c305c2014-07-01 23:34:41 +02001975 public int getContentHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001976 return mContentHeight;
1977 }
1978
1979 private void updateContentHeight() {
1980 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08001981 float previousPaddingRequest = mPaddingBetweenElements;
1982 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07001983 int numShownItems = 0;
1984 boolean finish = false;
Adrian Roos7a9551a2017-01-11 12:27:49 -08001985 int maxDisplayedNotifications = mAmbientState.isDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07001986 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08001987 : mMaxDisplayedNotifications;
1988
Selim Cinek67b22602014-03-10 15:40:16 +01001989 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08001990 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Selim Cinek281c2022016-10-13 19:14:43 -07001991 if (expandableView.getVisibility() != View.GONE
1992 && !expandableView.hasNoContentHeight()) {
Adrian Roos7d062c42017-03-30 15:11:43 -07001993 boolean limitReached = maxDisplayedNotifications != -1
1994 && numShownItems >= maxDisplayedNotifications;
Selim Cinekbe2c4432017-05-30 12:11:09 -07001995 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isDark()
1996 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07001997 && expandableView instanceof ExpandableNotificationRow
1998 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
1999 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002000 expandableView = mShelf;
2001 finish = true;
2002 }
Selim Cinek42357e02016-02-24 18:48:01 -08002003 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002004 float padding;
2005 if (increasedPaddingAmount >= 0.0f) {
2006 padding = (int) NotificationUtils.interpolate(
2007 previousPaddingRequest,
2008 mIncreasedPaddingBetweenElements,
2009 increasedPaddingAmount);
2010 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002011 mPaddingBetweenElements,
2012 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002013 increasedPaddingAmount);
2014 } else {
2015 int ownPadding = (int) NotificationUtils.interpolate(
2016 0,
2017 mPaddingBetweenElements,
2018 1.0f + increasedPaddingAmount);
2019 if (previousPaddingAmount > 0.0f) {
2020 padding = (int) NotificationUtils.interpolate(
2021 ownPadding,
2022 mIncreasedPaddingBetweenElements,
2023 previousPaddingAmount);
2024 } else {
2025 padding = ownPadding;
2026 }
2027 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002028 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002029 if (height != 0) {
2030 height += padding;
2031 }
2032 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002033 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002034 numShownItems++;
2035 if (finish) {
2036 break;
2037 }
Selim Cinek67b22602014-03-10 15:40:16 +01002038 }
2039 }
Anthony Chen9fe1ee72017-04-07 13:53:37 -07002040 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002041 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002042 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002043 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002044 }
2045
Adrian Roos7d062c42017-03-30 15:11:43 -07002046 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002047 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002048 }
2049
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002050 @Override
Selim Cinekbe2c4432017-05-30 12:11:09 -07002051 public boolean hasPulsingNotifications() {
Adrian Roos7d062c42017-03-30 15:11:43 -07002052 return mPulsing != null;
2053 }
2054
Selim Cinekc22fff62016-05-20 12:44:30 -07002055 private void updateScrollability() {
2056 boolean scrollable = getScrollRange() > 0;
2057 if (scrollable != mScrollable) {
2058 mScrollable = scrollable;
2059 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002060 updateForwardAndBackwardScrollability();
2061 }
2062 }
2063
2064 private void updateForwardAndBackwardScrollability() {
2065 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2066 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2067 boolean changed = forwardScrollable != mForwardScrollable
2068 || backwardsScrollable != mBackwardScrollable;
2069 mForwardScrollable = forwardScrollable;
2070 mBackwardScrollable = backwardsScrollable;
2071 if (changed) {
2072 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002073 }
Selim Cinek67b22602014-03-10 15:40:16 +01002074 }
2075
Selim Cinek6811d722016-01-19 17:53:12 -08002076 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002077 // No need to update the background color if it's not being drawn.
2078 if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002079 return;
2080 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002081
Selim Cinek6811d722016-01-19 17:53:12 -08002082 updateBackgroundBounds();
2083 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002084 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2085 || areBoundsAnimating();
2086 if (!isExpanded()) {
2087 abortBackgroundAnimators();
2088 animate = false;
2089 }
2090 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002091 startBackgroundAnimation();
2092 } else {
2093 mCurrentBounds.set(mBackgroundBounds);
2094 applyCurrentBackgroundBounds();
2095 }
2096 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002097 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002098 }
Selim Cinek614576e2016-01-20 10:54:09 -08002099 mAnimateNextBackgroundBottom = false;
2100 mAnimateNextBackgroundTop = false;
2101 }
2102
Selim Cinek54680902016-10-19 16:49:44 -07002103 private void abortBackgroundAnimators() {
2104 if (mBottomAnimator != null) {
2105 mBottomAnimator.cancel();
2106 }
2107 if (mTopAnimator != null) {
2108 mTopAnimator.cancel();
2109 }
2110 }
2111
Selim Cinek614576e2016-01-20 10:54:09 -08002112 private boolean areBoundsAnimating() {
2113 return mBottomAnimator != null || mTopAnimator != null;
2114 }
2115
2116 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002117 // left and right are always instantly applied
2118 mCurrentBounds.left = mBackgroundBounds.left;
2119 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002120 startBottomAnimation();
2121 startTopAnimation();
2122 }
2123
2124 private void startTopAnimation() {
2125 int previousEndValue = mEndAnimationRect.top;
2126 int newEndValue = mBackgroundBounds.top;
2127 ObjectAnimator previousAnimator = mTopAnimator;
2128 if (previousAnimator != null && previousEndValue == newEndValue) {
2129 return;
2130 }
2131 if (!mAnimateNextBackgroundTop) {
2132 // just a local update was performed
2133 if (previousAnimator != null) {
2134 // we need to increase all animation keyframes of the previous animator by the
2135 // relative change to the end value
2136 int previousStartValue = mStartAnimationRect.top;
2137 PropertyValuesHolder[] values = previousAnimator.getValues();
2138 values[0].setIntValues(previousStartValue, newEndValue);
2139 mStartAnimationRect.top = previousStartValue;
2140 mEndAnimationRect.top = newEndValue;
2141 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2142 return;
2143 } else {
2144 // no new animation needed, let's just apply the value
2145 setBackgroundTop(newEndValue);
2146 return;
2147 }
2148 }
2149 if (previousAnimator != null) {
2150 previousAnimator.cancel();
2151 }
2152 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2153 mCurrentBounds.top, newEndValue);
2154 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2155 animator.setInterpolator(interpolator);
2156 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2157 // remove the tag when the animation is finished
2158 animator.addListener(new AnimatorListenerAdapter() {
2159 @Override
2160 public void onAnimationEnd(Animator animation) {
2161 mStartAnimationRect.top = -1;
2162 mEndAnimationRect.top = -1;
2163 mTopAnimator = null;
2164 }
2165 });
2166 animator.start();
2167 mStartAnimationRect.top = mCurrentBounds.top;
2168 mEndAnimationRect.top = newEndValue;
2169 mTopAnimator = animator;
2170 }
2171
2172 private void startBottomAnimation() {
2173 int previousStartValue = mStartAnimationRect.bottom;
2174 int previousEndValue = mEndAnimationRect.bottom;
2175 int newEndValue = mBackgroundBounds.bottom;
2176 ObjectAnimator previousAnimator = mBottomAnimator;
2177 if (previousAnimator != null && previousEndValue == newEndValue) {
2178 return;
2179 }
2180 if (!mAnimateNextBackgroundBottom) {
2181 // just a local update was performed
2182 if (previousAnimator != null) {
2183 // we need to increase all animation keyframes of the previous animator by the
2184 // relative change to the end value
2185 PropertyValuesHolder[] values = previousAnimator.getValues();
2186 values[0].setIntValues(previousStartValue, newEndValue);
2187 mStartAnimationRect.bottom = previousStartValue;
2188 mEndAnimationRect.bottom = newEndValue;
2189 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2190 return;
2191 } else {
2192 // no new animation needed, let's just apply the value
2193 setBackgroundBottom(newEndValue);
2194 return;
2195 }
2196 }
2197 if (previousAnimator != null) {
2198 previousAnimator.cancel();
2199 }
2200 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2201 mCurrentBounds.bottom, newEndValue);
2202 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2203 animator.setInterpolator(interpolator);
2204 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2205 // remove the tag when the animation is finished
2206 animator.addListener(new AnimatorListenerAdapter() {
2207 @Override
2208 public void onAnimationEnd(Animator animation) {
2209 mStartAnimationRect.bottom = -1;
2210 mEndAnimationRect.bottom = -1;
2211 mBottomAnimator = null;
2212 }
2213 });
2214 animator.start();
2215 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2216 mEndAnimationRect.bottom = newEndValue;
2217 mBottomAnimator = animator;
2218 }
2219
2220 private void setBackgroundTop(int top) {
2221 mCurrentBounds.top = top;
2222 applyCurrentBackgroundBounds();
2223 }
2224
2225 public void setBackgroundBottom(int bottom) {
2226 mCurrentBounds.bottom = bottom;
2227 applyCurrentBackgroundBounds();
2228 }
2229
2230 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002231 // If the background of the notification is not being drawn, then there is no need to
2232 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2233 if (!mShouldDrawNotificationBackground) {
2234 return;
2235 }
2236
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002237 mScrimController.setExcludedBackgroundArea(
Selim Cinek48ff9b42016-11-09 19:31:51 -08002238 mFadingOut || mParentNotFullyVisible || mAmbientState.isDark() || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002239 : mCurrentBounds);
Selim Cinek515b2032017-11-15 10:20:19 -08002240 updateBackgroundPath();
Selim Cinek614576e2016-01-20 10:54:09 -08002241 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002242 }
2243
Selim Cinek515b2032017-11-15 10:20:19 -08002244 private void updateBackgroundPath() {
2245 mBackgroundPath.reset();
2246 float topRoundness = 0;
2247 if (mFirstVisibleBackgroundChild != null) {
2248 topRoundness = mFirstVisibleBackgroundChild.getCurrentBackgroundRadiusTop();
2249 }
2250 topRoundness = onKeyguard() ? mCornerRadius : topRoundness;
2251 float bottomRoundNess = mCornerRadius;
2252 mBackgroundRadii[0] = topRoundness;
2253 mBackgroundRadii[1] = topRoundness;
2254 mBackgroundRadii[2] = topRoundness;
2255 mBackgroundRadii[3] = topRoundness;
2256 mBackgroundRadii[4] = bottomRoundNess;
2257 mBackgroundRadii[5] = bottomRoundNess;
2258 mBackgroundRadii[6] = bottomRoundNess;
2259 mBackgroundRadii[7] = bottomRoundNess;
2260 mBackgroundPath.addRoundRect(mCurrentSidePadding, mCurrentBounds.top,
2261 getWidth() - mCurrentSidePadding, mCurrentBounds.bottom, mBackgroundRadii,
2262 Path.Direction.CCW);
2263 }
2264
Selim Cinek6811d722016-01-19 17:53:12 -08002265 /**
2266 * Update the background bounds to the new desired bounds
2267 */
2268 private void updateBackgroundBounds() {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01002269 if (mAmbientState.isPanelFullWidth()) {
2270 mBackgroundBounds.left = 0;
2271 mBackgroundBounds.right = getWidth();
2272 } else {
2273 getLocationInWindow(mTempInt2);
2274 mBackgroundBounds.left = mTempInt2[0];
2275 mBackgroundBounds.right = mTempInt2[0] + getWidth();
2276 }
Selim Cinek515b2032017-11-15 10:20:19 -08002277 mBackgroundBounds.left += mCurrentSidePadding;
2278 mBackgroundBounds.right -= mCurrentSidePadding;
Selim Cinek614576e2016-01-20 10:54:09 -08002279 if (!mIsExpanded) {
2280 mBackgroundBounds.top = 0;
2281 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002282 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002283 }
2284 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002285 int top = 0;
2286 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002287 // Round Y up to avoid seeing the background during animation
2288 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002289 if (mAnimateNextBackgroundTop
2290 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2291 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002292 // we're ending up at the same location as we are now, lets just skip the animation
2293 top = finalTranslationY;
2294 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002295 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002296 }
2297 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002298 ActivatableNotificationView lastView =
2299 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2300 ? mShelf
2301 : mLastVisibleBackgroundChild;
2302 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002303 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002304 int finalTranslationY;
2305 if (lastView == mShelf) {
2306 finalTranslationY = (int) mShelf.getTranslationY();
2307 } else {
2308 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2309 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002310 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002311 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek6811d722016-01-19 17:53:12 -08002312 finalBottom = Math.min(finalBottom, getHeight());
Selim Cinek614576e2016-01-20 10:54:09 -08002313 if (mAnimateNextBackgroundBottom
2314 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2315 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002316 // we're ending up at the same location as we are now, lets just skip the animation
2317 bottom = finalBottom;
2318 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002319 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2320 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002321 bottom = Math.min(bottom, getHeight());
2322 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002323 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002324 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002325 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002326 }
Selim Cinek355652a2016-12-07 13:32:12 -08002327 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002328 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002329 } else {
2330 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002331 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002332 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002333 mBackgroundBounds.top = top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002334 mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
Selim Cinek6811d722016-01-19 17:53:12 -08002335 }
2336
Selim Cinek614576e2016-01-20 10:54:09 -08002337 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2338 int childCount = getChildCount();
2339 for (int i = 0; i < childCount; i++) {
2340 View child = getChildAt(i);
2341 if (child.getVisibility() != View.GONE
2342 && child instanceof ExpandableNotificationRow) {
2343 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2344 if (row.isPinned()) {
2345 return row;
2346 }
2347 }
2348 }
2349 return null;
2350 }
2351
2352 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002353 int childCount = getChildCount();
2354 for (int i = childCount - 1; i >= 0; i--) {
2355 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002356 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2357 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002358 return (ActivatableNotificationView) child;
2359 }
2360 }
2361 return null;
2362 }
2363
Selim Cinek614576e2016-01-20 10:54:09 -08002364 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002365 int childCount = getChildCount();
2366 for (int i = 0; i < childCount; i++) {
2367 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002368 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2369 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002370 return (ActivatableNotificationView) child;
2371 }
2372 }
2373 return null;
2374 }
2375
Selim Cinek67b22602014-03-10 15:40:16 +01002376 /**
2377 * Fling the scroll view
2378 *
2379 * @param velocityY The initial velocity in the Y direction. Positive
2380 * numbers mean that the finger/cursor is moving down the screen,
2381 * which means we want to scroll towards the top.
2382 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002383 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002384 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002385 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002386
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002387 float topAmount = getCurrentOverScrollAmount(true);
2388 float bottomAmount = getCurrentOverScrollAmount(false);
2389 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002390 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002391 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002392 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002393 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002394 * mOverflingDistance + topAmount;
2395 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002396 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002397 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002398 mMaxOverScroll = Math.abs(velocityY) / 1000f
2399 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2400 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002401 } else {
2402 // it will be set once we reach the boundary
2403 mMaxOverScroll = 0.0f;
2404 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002405 int minScrollY = Math.max(0, scrollRange);
2406 if (mExpandedInThisMotion) {
2407 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2408 }
Selim Cinek9212de82017-02-06 16:04:28 -08002409 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2410 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002411
Selim Cinek9212de82017-02-06 16:04:28 -08002412 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002413 }
2414 }
2415
Selim Cinek1408eb52014-06-02 14:45:38 +02002416 /**
2417 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2418 * overScroll view (i.e QS).
2419 */
2420 private boolean shouldOverScrollFling(int initialVelocity) {
2421 float topOverScroll = getCurrentOverScrollAmount(true);
2422 return mScrolledToTopOnFirstDown
2423 && !mExpandedInThisMotion
2424 && topOverScroll > mMinTopOverScrollToEscape
2425 && initialVelocity > 0;
2426 }
2427
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002428 /**
2429 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2430 * account.
2431 *
2432 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002433 * @param animate whether to animate the change
2434 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2435 * {@code qsHeight} is the final top padding
2436 */
Jason Monk16ac3772016-02-10 15:39:21 -05002437 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002438 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002439 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002440 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002441 if (topPadding + minStackHeight > getHeight()) {
2442 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002443 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002444 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002445 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002446 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002447 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002448 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002449 }
2450
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002451 public int getLayoutMinHeight() {
Anthony Chen9e05d462017-04-07 10:10:21 -07002452 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002453 }
2454
Selim Cineka981d082016-08-03 14:45:31 -07002455 public int getFirstChildIntrinsicHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002456 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002457 int firstChildMinHeight = firstChild != null
2458 ? firstChild.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002459 : mEmptyShadeView != null
Selim Cineka981d082016-08-03 14:45:31 -07002460 ? mEmptyShadeView.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002461 : mCollapsedSize;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002462 if (mOwnScrollY > 0) {
2463 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2464 }
Selim Cinek94c2d822016-07-13 18:50:04 -07002465 return firstChildMinHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002466 }
2467
2468 public float getTopPaddingOverflow() {
2469 return mTopPaddingOverflow;
2470 }
2471
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002472 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002473 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002474 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002475 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002476 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002477 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002478 shelfHeight = mShelf.getIntrinsicHeight();
2479 }
2480 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002481 }
2482
Selim Cinek1408eb52014-06-02 14:45:38 +02002483 private int clampPadding(int desiredPadding) {
2484 return Math.max(desiredPadding, mIntrinsicPadding);
2485 }
2486
Selim Cinekfed1ab62014-06-17 14:10:33 -07002487 private float getRubberBandFactor(boolean onTop) {
2488 if (!onTop) {
2489 return RUBBER_BAND_FACTOR_NORMAL;
2490 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002491 if (mExpandedInThisMotion) {
2492 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002493 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002494 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2495 } else if (mScrolledToTopOnFirstDown) {
2496 return 1.0f;
2497 }
2498 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002499 }
2500
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002501 /**
2502 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2503 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2504 * overscroll view (e.g. expand QS).
2505 */
2506 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002507 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002508 || !mScrolledToTopOnFirstDown;
2509 }
2510
Selim Cinek67b22602014-03-10 15:40:16 +01002511 private void endDrag() {
2512 setIsBeingDragged(false);
2513
2514 recycleVelocityTracker();
2515
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002516 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2517 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2518 }
2519 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2520 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2521 }
Selim Cinek67b22602014-03-10 15:40:16 +01002522 }
2523
Jorim Jaggi56306252014-07-03 00:40:09 +02002524 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2525 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2526 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2527 }
2528
Selim Cinek67b22602014-03-10 15:40:16 +01002529 @Override
2530 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002531 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002532 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002533 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002534 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002535 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2536 }
Selim Cinek67b22602014-03-10 15:40:16 +01002537 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002538 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002539 scrollWantsIt = onInterceptTouchEventScroll(ev);
2540 }
2541 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002542 if (!mIsBeingDragged
2543 && !mExpandingNotification
2544 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002545 && !mOnlyScrollingInThisMotion
2546 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002547 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2548 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002549 // Check if we need to clear any snooze leavebehinds
2550 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002551 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07002552 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2553 && !scrollWantsIt) {
2554 mCheckForLeavebehind = false;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002555 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
2556 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2557 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07002558 }
2559 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2560 mCheckForLeavebehind = true;
2561 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002562 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2563 }
2564
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002565 private void handleEmptySpaceClick(MotionEvent ev) {
2566 switch (ev.getActionMasked()) {
2567 case MotionEvent.ACTION_MOVE:
2568 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2569 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2570 mTouchIsClick = false;
2571 }
2572 break;
2573 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002574 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002575 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2576 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2577 }
2578 break;
2579 }
2580 }
2581
Selim Cinek1408eb52014-06-02 14:45:38 +02002582 private void initDownStates(MotionEvent ev) {
2583 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2584 mExpandedInThisMotion = false;
2585 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002586 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002587 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002588 mTouchIsClick = true;
2589 mInitialTouchX = ev.getX();
2590 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002591 }
Selim Cinek67b22602014-03-10 15:40:16 +01002592 }
2593
Selim Cinekef5127e2015-12-21 16:55:58 -08002594 public void setChildTransferInProgress(boolean childTransferInProgress) {
2595 mChildTransferInProgress = childTransferInProgress;
2596 }
2597
Christoph Studer068f5922014-04-08 17:43:07 -04002598 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002599 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002600 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002601 // we only call our internal methods if this is actually a removal and not just a
2602 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002603 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002604 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002605 }
2606 }
2607
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002608 @Override
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002609 public void cleanUpViewState(View child) {
2610 if (child == mTranslatingParentView) {
2611 mTranslatingParentView = null;
2612 }
2613 mCurrentStackScrollState.removeViewStateForView(child);
2614 }
2615
Selim Cinek197823d2016-03-24 13:06:00 -07002616 @Override
2617 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2618 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2619 if (disallowIntercept) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002620 cancelLongPress();
Selim Cinek197823d2016-03-24 13:06:00 -07002621 }
2622 }
2623
Selim Cineka5703182016-05-11 21:23:16 -04002624 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002625 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002626 // This is only a position change, don't do anything special
2627 return;
2628 }
Selim Cinekd1395642016-04-28 12:22:42 -07002629 ExpandableView expandableView = (ExpandableView) child;
2630 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002631 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002632 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002633 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002634 if (animationGenerated) {
2635 if (!mSwipedOutViews.contains(child)) {
Selim Cineka5703182016-05-11 21:23:16 -04002636 container.getOverlay().add(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002637 } else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002638 container.addTransientView(child, 0);
2639 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002640 }
2641 } else {
2642 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002643 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002644 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002645
Selim Cineke9bad242016-06-15 11:46:37 -07002646 focusNextViewIfFocused(child);
2647 }
2648
2649 private void focusNextViewIfFocused(View view) {
2650 if (view instanceof ExpandableNotificationRow) {
2651 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2652 if (row.shouldRefocusOnDismiss()) {
2653 View nextView = row.getChildAfterViewWhenDismissed();
2654 if (nextView == null) {
2655 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2656 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2657 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002658 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002659 }
2660 if (nextView != null) {
2661 nextView.requestAccessibilityFocus();
2662 }
2663 }
2664 }
2665
Selim Cinekc27437b2014-05-14 10:23:33 +02002666 }
2667
Selim Cinekb5605e52015-02-20 18:21:41 +01002668 private boolean isChildInGroup(View child) {
2669 return child instanceof ExpandableNotificationRow
2670 && mGroupManager.isChildInGroupWithSummary(
2671 ((ExpandableNotificationRow) child).getStatusBarNotification());
2672 }
2673
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002674 /**
2675 * Generate a remove animation for a child view.
2676 *
2677 * @param child The view to generate the remove animation for.
2678 * @return Whether an animation was generated.
2679 */
2680 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002681 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002682 mAddedHeadsUpChildren.remove(child);
2683 return false;
2684 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002685 if (isClickedHeadsUp(child)) {
2686 // An animation is already running, add it to the Overlay
2687 mClearOverlayViewsWhenFinished.add(child);
2688 return true;
2689 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002690 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002691 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002692 // Generate Animations
2693 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002694 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002695 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002696 } else {
2697 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002698 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002699 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002700 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002701 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002702 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002703 }
2704
Selim Cinek0fccc722015-07-29 17:04:36 -07002705 private boolean isClickedHeadsUp(View child) {
2706 return HeadsUpManager.isClickedHeadsUpNotification(child);
2707 }
2708
Selim Cineke0890e52015-06-17 11:17:08 -07002709 /**
2710 * Remove a removed child view from the heads up animations if it was just added there
2711 *
2712 * @return whether any child was removed from the list to animate
2713 */
2714 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2715 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002716 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2717 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002718 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002719 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002720 mTmpList.add(eventPair);
2721 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002722 }
2723 }
Selim Cineke0890e52015-06-17 11:17:08 -07002724 if (hasAddEvent) {
2725 // This child was just added lets remove all events.
2726 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002727 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002728 }
2729 mTmpList.clear();
2730 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002731 }
2732
Selim Cinek572bbd42014-04-25 16:43:27 +02002733 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002734 * @param child the child to query
2735 * @return whether a view is not a top level child but a child notification and that group is
2736 * not expanded
2737 */
2738 private boolean isChildInInvisibleGroup(View child) {
2739 if (child instanceof ExpandableNotificationRow) {
2740 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2741 ExpandableNotificationRow groupSummary =
2742 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2743 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002744 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002745 }
2746 }
2747 return false;
2748 }
2749
2750 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002751 * Updates the scroll position when a child was removed
2752 *
2753 * @param removedChild the removed child
2754 */
Selim Cinek61633a82016-01-25 15:54:10 -08002755 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002756 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002757 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2758 int padding;
2759 if (increasedPaddingAmount >= 0) {
2760 padding = (int) NotificationUtils.interpolate(
2761 mPaddingBetweenElements,
2762 mIncreasedPaddingBetweenElements,
2763 increasedPaddingAmount);
2764 } else {
2765 padding = (int) NotificationUtils.interpolate(
2766 0,
2767 mPaddingBetweenElements,
2768 1.0f + increasedPaddingAmount);
2769 }
Selim Cinek61633a82016-01-25 15:54:10 -08002770 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002771 int endPosition = startingPosition + childHeight;
2772 if (endPosition <= mOwnScrollY) {
2773 // This child is fully scrolled of the top, so we have to deduct its height from the
2774 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002775 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002776 } else if (startingPosition < mOwnScrollY) {
2777 // This child is currently being scrolled into, set the scroll position to the start of
2778 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002779 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002780 }
2781 }
2782
Selim Cinekd7c4e002014-07-04 18:36:42 +02002783 private int getIntrinsicHeight(View view) {
2784 if (view instanceof ExpandableView) {
2785 ExpandableView expandableView = (ExpandableView) view;
2786 return expandableView.getIntrinsicHeight();
2787 }
2788 return view.getHeight();
2789 }
2790
Selim Cinek1f624952017-06-08 19:11:50 -07002791 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002792 ExpandableNotificationRow childInGroup = null;
2793 ExpandableNotificationRow requestedRow = null;
2794 if (isChildInGroup(requestedView)) {
2795 // We're asking for a child in a group. Calculate the position of the parent first,
2796 // then within the parent.
2797 childInGroup = (ExpandableNotificationRow) requestedView;
2798 requestedView = requestedRow = childInGroup.getNotificationParent();
2799 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002800 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002801 float previousPaddingRequest = mPaddingBetweenElements;
2802 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002803 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002804 ExpandableView child = (ExpandableView) getChildAt(i);
2805 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002806 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002807 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002808 float padding;
2809 if (increasedPaddingAmount >= 0.0f) {
2810 padding = (int) NotificationUtils.interpolate(
2811 previousPaddingRequest,
2812 mIncreasedPaddingBetweenElements,
2813 increasedPaddingAmount);
2814 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002815 mPaddingBetweenElements,
2816 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002817 increasedPaddingAmount);
2818 } else {
2819 int ownPadding = (int) NotificationUtils.interpolate(
2820 0,
2821 mPaddingBetweenElements,
2822 1.0f + increasedPaddingAmount);
2823 if (previousPaddingAmount > 0.0f) {
2824 padding = (int) NotificationUtils.interpolate(
2825 ownPadding,
2826 mIncreasedPaddingBetweenElements,
2827 previousPaddingAmount);
2828 } else {
2829 padding = ownPadding;
2830 }
2831 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08002832 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002833 if (position != 0) {
2834 position += padding;
2835 }
2836 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002837 }
Adrian Roos4a579672016-05-24 16:54:37 -07002838 if (child == requestedView) {
2839 if (requestedRow != null) {
2840 position += requestedRow.getPositionOfChild(childInGroup);
2841 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002842 return position;
2843 }
Selim Cinek61633a82016-01-25 15:54:10 -08002844 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02002845 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002846 }
2847 }
2848 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02002849 }
2850
2851 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002852 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02002853 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002854 onViewAddedInternal(child);
2855 }
2856
Selim Cinek614576e2016-01-20 10:54:09 -08002857 private void updateFirstAndLastBackgroundViews() {
2858 ActivatableNotificationView firstChild = getFirstChildWithBackground();
2859 ActivatableNotificationView lastChild = getLastChildWithBackground();
Selim Cinek0fe07392017-11-09 13:26:34 -08002860 boolean firstChanged = firstChild != mFirstVisibleBackgroundChild;
2861 boolean lastChanged = lastChild != mLastVisibleBackgroundChild;
Selim Cinek614576e2016-01-20 10:54:09 -08002862 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek0fe07392017-11-09 13:26:34 -08002863 mAnimateNextBackgroundTop = firstChanged;
2864 mAnimateNextBackgroundBottom = lastChanged;
Selim Cinek614576e2016-01-20 10:54:09 -08002865 } else {
2866 mAnimateNextBackgroundTop = false;
2867 mAnimateNextBackgroundBottom = false;
2868 }
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002869 if (firstChanged && mFirstVisibleBackgroundChild != null
2870 && !mFirstVisibleBackgroundChild.isRemoved()) {
2871 mFirstVisibleBackgroundChild.setTopRoundness(0.0f,
2872 mFirstVisibleBackgroundChild.isShown());
Selim Cinek0fe07392017-11-09 13:26:34 -08002873 }
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002874 if (lastChanged && mLastVisibleBackgroundChild != null
2875 && !mLastVisibleBackgroundChild.isRemoved()) {
Selim Cinek2871bef2017-11-22 08:40:00 -08002876 mLastVisibleBackgroundChild.setBottomRoundness(0.0f,
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002877 mLastVisibleBackgroundChild.isShown());
Selim Cinek0fe07392017-11-09 13:26:34 -08002878 }
Selim Cinek614576e2016-01-20 10:54:09 -08002879 mFirstVisibleBackgroundChild = firstChild;
2880 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08002881 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek515b2032017-11-15 10:20:19 -08002882 applyRoundedNess();
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002883 }
2884
2885 private void applyRoundedNess() {
Selim Cinek0fe07392017-11-09 13:26:34 -08002886 if (mFirstVisibleBackgroundChild != null) {
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002887 mFirstVisibleBackgroundChild.setTopRoundness(
2888 mStatusBarState == StatusBarState.KEYGUARD ? 1.0f : 0.0f,
2889 mFirstVisibleBackgroundChild.isShown()
2890 && !mChildrenToAddAnimated.contains(mFirstVisibleBackgroundChild));
Selim Cinek0fe07392017-11-09 13:26:34 -08002891 }
2892 if (mLastVisibleBackgroundChild != null) {
Selim Cinek2871bef2017-11-22 08:40:00 -08002893 mLastVisibleBackgroundChild.setBottomRoundness(1.0f,
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002894 mLastVisibleBackgroundChild.isShown()
2895 && !mChildrenToAddAnimated.contains(mLastVisibleBackgroundChild));
Selim Cinek0fe07392017-11-09 13:26:34 -08002896 }
Selim Cinek515b2032017-11-15 10:20:19 -08002897 updateBackgroundPath();
2898 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002899 }
2900
Selim Cinekb5605e52015-02-20 18:21:41 +01002901 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002902 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02002903 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00002904 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002905 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02002906 updateChronometerForChild(child);
Selim Cinek515b2032017-11-15 10:20:19 -08002907 updateCurrentSidePaddings(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002908 }
2909
Selim Cinekd06c41c2015-07-06 14:51:36 -07002910 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002911 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002912 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002913 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07002914 }
2915 }
2916
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002917 @Override
Selim Cinekd1395642016-04-28 12:22:42 -07002918 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
2919 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01002920 }
2921
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002922 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01002923 public void notifyGroupChildAdded(View row) {
2924 onViewAddedInternal(row);
2925 }
2926
Jorim Jaggi75c95042014-05-16 19:09:59 +02002927 public void setAnimationsEnabled(boolean animationsEnabled) {
2928 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02002929 updateNotificationAnimationStates();
2930 }
2931
2932 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002933 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08002934 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02002935 int childCount = getChildCount();
2936 for (int i = 0; i < childCount; i++) {
2937 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07002938 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02002939 updateAnimationState(running, child);
2940 }
2941 }
2942
Selim Cinek51ae05d2014-09-09 15:51:38 +02002943 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002944 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08002945 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002946 }
2947
2948
Selim Cinekcab4a602014-09-03 14:47:57 +02002949 private void updateAnimationState(boolean running, View child) {
2950 if (child instanceof ExpandableNotificationRow) {
2951 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2952 row.setIconAnimationRunning(running);
2953 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02002954 }
2955
2956 public boolean isAddOrRemoveAnimationPending() {
2957 return mNeedsAnimation
2958 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
2959 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002960
2961 @Override
Jorim Jaggif6411742014-08-05 17:10:43 +00002962 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002963 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002964 // Generate Animations
2965 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00002966 if (fromMoreCard) {
2967 mFromMoreCardAdditions.add(child);
2968 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002969 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02002970 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08002971 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07002972 mAddedHeadsUpChildren.add(child);
2973 mChildrenToAddAnimated.remove(child);
2974 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002975 }
2976
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002977 @Override
Selim Cinek572bbd42014-04-25 16:43:27 +02002978 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04002979 int currentIndex = indexOfChild(child);
2980 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002981 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08002982 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02002983 removeView(child);
2984 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08002985 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02002986 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04002987 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002988 mChildrenChangingPositions.add(child);
2989 mNeedsAnimation = true;
2990 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002991 }
2992 }
2993
Selim Cinekf4c19962014-05-01 21:55:31 +02002994 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002995 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002996 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002997 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002998 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002999 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003000 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003001 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
3002 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003003 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003004 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003005 updateViewShadows();
Selim Cinekf4c19962014-05-01 21:55:31 +02003006 } else {
3007 applyCurrentState();
3008 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003009 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003010 }
3011
3012 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003013 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003014 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003015 generateChildAdditionEvents();
3016 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02003017 generateSnapBackEvents();
3018 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003019 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003020 generateActivateEvent();
3021 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003022 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003023 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003024 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003025 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003026 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003027 generateAnimateEverythingEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003028 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003029 }
3030
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003031 private void generateHeadsUpAnimationEvents() {
3032 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003033 ExpandableNotificationRow row = eventPair.first;
3034 boolean isHeadsUp = eventPair.second;
3035 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3036 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003037 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003038 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003039 type = row.wasJustClicked()
3040 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3041 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003042 if (row.isChildInGroup()) {
3043 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003044 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003045 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003046 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08003047 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04003048 if (viewState == null) {
3049 // A view state was never generated for this view, so we don't need to animate
3050 // this. This may happen with notification children.
3051 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003052 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003053 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3054 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3055 // Our custom add animation
3056 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3057 } else {
3058 // Normal add animation
3059 type = AnimationEvent.ANIMATION_TYPE_ADD;
3060 }
3061 onBottom = !pinnedAndClosed;
3062 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003063 }
3064 AnimationEvent event = new AnimationEvent(row, type);
3065 event.headsUpFromBottom = onBottom;
3066 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003067 }
3068 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003069 mAddedHeadsUpChildren.clear();
3070 }
3071
Selim Cinekbbcebde2016-11-09 18:28:20 -08003072 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003073 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3074 return false;
3075 }
3076 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003077 }
3078
Selim Cinekb5605e52015-02-20 18:21:41 +01003079 private void generateGroupExpansionEvent() {
3080 // Generate a group expansion/collapsing event if there is such a group at all
3081 if (mExpandedGroupView != null) {
3082 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3083 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3084 mExpandedGroupView = null;
3085 }
3086 }
3087
Selim Cineka5e211b2014-08-11 17:35:48 +02003088 private void generateViewResizeEvent() {
3089 if (mNeedViewResizeAnimation) {
3090 mAnimationEvents.add(
3091 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3092 }
3093 mNeedViewResizeAnimation = false;
3094 }
3095
Selim Cinekeb973562014-05-02 17:07:49 +02003096 private void generateSnapBackEvents() {
3097 for (View child : mSnappedBackChildren) {
3098 mAnimationEvents.add(new AnimationEvent(child,
3099 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3100 }
3101 mSnappedBackChildren.clear();
3102 }
3103
3104 private void generateDragEvents() {
3105 for (View child : mDragAnimPendingChildren) {
3106 mAnimationEvents.add(new AnimationEvent(child,
3107 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3108 }
3109 mDragAnimPendingChildren.clear();
3110 }
3111
Selim Cinek572bbd42014-04-25 16:43:27 +02003112 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003113 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003114 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003115
3116 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003117 float removedTranslation = child.getTranslationY();
3118 boolean ignoreChildren = true;
3119 if (child instanceof ExpandableNotificationRow) {
3120 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3121 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3122 removedTranslation = row.getTranslationWhenRemoved();
3123 ignoreChildren = false;
3124 }
Selim Cinek51052042017-07-04 12:07:55 +02003125 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003126 }
Selim Cinek51052042017-07-04 12:07:55 +02003127 if (!childWasSwipedOut) {
3128 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003129 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Selim Cinek51052042017-07-04 12:07:55 +02003130 }
3131 int animationType = childWasSwipedOut
3132 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3133 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3134 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003135 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3136 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003137 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003138 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003139 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003140 mChildrenToRemoveAnimated.clear();
3141 }
3142
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003143 private void generatePositionChangeEvents() {
3144 for (View child : mChildrenChangingPositions) {
3145 mAnimationEvents.add(new AnimationEvent(child,
3146 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3147 }
3148 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003149 if (mGenerateChildOrderChangedEvent) {
3150 mAnimationEvents.add(new AnimationEvent(null,
3151 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3152 mGenerateChildOrderChangedEvent = false;
3153 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003154 }
3155
Selim Cinek572bbd42014-04-25 16:43:27 +02003156 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003157 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003158 if (mFromMoreCardAdditions.contains(child)) {
3159 mAnimationEvents.add(new AnimationEvent(child,
3160 AnimationEvent.ANIMATION_TYPE_ADD,
3161 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3162 } else {
3163 mAnimationEvents.add(new AnimationEvent(child,
3164 AnimationEvent.ANIMATION_TYPE_ADD));
3165 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003166 }
3167 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003168 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003169 }
3170
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003171 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003172 if (mTopPaddingNeedsAnimation) {
3173 mAnimationEvents.add(
3174 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
3175 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003176 mTopPaddingNeedsAnimation = false;
3177 }
3178
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003179 private void generateActivateEvent() {
3180 if (mActivateNeedsAnimation) {
3181 mAnimationEvents.add(
3182 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3183 }
3184 mActivateNeedsAnimation = false;
3185 }
3186
Selim Cinekd9acca52014-09-01 22:33:25 +02003187 private void generateAnimateEverythingEvent() {
3188 if (mEverythingNeedsAnimation) {
3189 mAnimationEvents.add(
3190 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3191 }
3192 mEverythingNeedsAnimation = false;
3193 }
3194
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003195 private void generateDimmedEvent() {
3196 if (mDimmedNeedsAnimation) {
3197 mAnimationEvents.add(
3198 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3199 }
3200 mDimmedNeedsAnimation = false;
3201 }
3202
Jorim Jaggiae441282014-08-01 02:45:18 +02003203 private void generateHideSensitiveEvent() {
3204 if (mHideSensitiveNeedsAnimation) {
3205 mAnimationEvents.add(
3206 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3207 }
3208 mHideSensitiveNeedsAnimation = false;
3209 }
3210
John Spurlockbf370992014-06-17 13:58:31 -04003211 private void generateDarkEvent() {
3212 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003213 AnimationEvent ev = new AnimationEvent(null,
3214 AnimationEvent.ANIMATION_TYPE_DARK,
3215 new AnimationFilter()
3216 .animateDark()
3217 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003218 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3219 mAnimationEvents.add(ev);
Selim Cinek972123d2016-05-03 14:25:58 -07003220 startBackgroundFadeIn();
John Spurlockbf370992014-06-17 13:58:31 -04003221 }
3222 mDarkNeedsAnimation = false;
3223 }
3224
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003225 private void generateGoToFullShadeEvent() {
3226 if (mGoToFullShadeNeedsAnimation) {
3227 mAnimationEvents.add(
3228 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3229 }
3230 mGoToFullShadeNeedsAnimation = false;
3231 }
3232
Selim Cinek67b22602014-03-10 15:40:16 +01003233 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003234 if (!isScrollingEnabled()) {
3235 return false;
3236 }
Selim Cinek67b22602014-03-10 15:40:16 +01003237 /*
3238 * This method JUST determines whether we want to intercept the motion.
3239 * If we return true, onMotionEvent will be called and we do the actual
3240 * scrolling there.
3241 */
3242
3243 /*
3244 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003245 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003246 * motion.
3247 */
3248 final int action = ev.getAction();
3249 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3250 return true;
3251 }
3252
Selim Cinek67b22602014-03-10 15:40:16 +01003253 switch (action & MotionEvent.ACTION_MASK) {
3254 case MotionEvent.ACTION_MOVE: {
3255 /*
3256 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003257 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003258 */
3259
3260 /*
3261 * Locally do absolute value. mLastMotionY is set to the y value
3262 * of the down event.
3263 */
3264 final int activePointerId = mActivePointerId;
3265 if (activePointerId == INVALID_POINTER) {
3266 // If we don't have a valid id, the touch down wasn't on content.
3267 break;
3268 }
3269
3270 final int pointerIndex = ev.findPointerIndex(activePointerId);
3271 if (pointerIndex == -1) {
3272 Log.e(TAG, "Invalid pointerId=" + activePointerId
3273 + " in onInterceptTouchEvent");
3274 break;
3275 }
3276
3277 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003278 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003279 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003280 final int xDiff = Math.abs(x - mDownX);
3281 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003282 setIsBeingDragged(true);
3283 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003284 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003285 initVelocityTrackerIfNotExists();
3286 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003287 }
3288 break;
3289 }
3290
3291 case MotionEvent.ACTION_DOWN: {
3292 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303293 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07003294 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01003295 setIsBeingDragged(false);
3296 recycleVelocityTracker();
3297 break;
3298 }
3299
3300 /*
3301 * Remember location of down touch.
3302 * ACTION_DOWN always refers to pointer index 0.
3303 */
3304 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003305 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003306 mActivePointerId = ev.getPointerId(0);
3307
3308 initOrResetVelocityTracker();
3309 mVelocityTracker.addMovement(ev);
3310 /*
3311 * If being flinged and user touches the screen, initiate drag;
3312 * otherwise don't. mScroller.isFinished should be false when
3313 * being flinged.
3314 */
3315 boolean isBeingDragged = !mScroller.isFinished();
3316 setIsBeingDragged(isBeingDragged);
3317 break;
3318 }
3319
3320 case MotionEvent.ACTION_CANCEL:
3321 case MotionEvent.ACTION_UP:
3322 /* Release the drag */
3323 setIsBeingDragged(false);
3324 mActivePointerId = INVALID_POINTER;
3325 recycleVelocityTracker();
3326 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08003327 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01003328 }
3329 break;
3330 case MotionEvent.ACTION_POINTER_UP:
3331 onSecondaryPointerUp(ev);
3332 break;
3333 }
3334
3335 /*
3336 * The only time we want to intercept motion events is if we are in the
3337 * drag mode.
3338 */
3339 return mIsBeingDragged;
3340 }
3341
Muyuan Li333a4fc2016-04-16 17:13:46 -07003342 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3343 return new StackScrollAlgorithm(context);
3344 }
3345
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003346 /**
3347 * @return Whether the specified motion event is actually happening over the content.
3348 */
3349 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003350 return isInContentBounds(event.getY());
3351 }
3352
3353 /**
3354 * @return Whether a y coordinate is inside the content.
3355 */
3356 public boolean isInContentBounds(float y) {
3357 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003358 }
3359
Selim Cinek67b22602014-03-10 15:40:16 +01003360 private void setIsBeingDragged(boolean isDragged) {
3361 mIsBeingDragged = isDragged;
3362 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003363 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003364 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003365 }
3366 }
3367
3368 @Override
3369 public void onWindowFocusChanged(boolean hasWindowFocus) {
3370 super.onWindowFocusChanged(hasWindowFocus);
3371 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003372 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003373 }
3374 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003375
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003376 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003377 public void clearChildFocus(View child) {
3378 super.clearChildFocus(child);
3379 if (mForcedScroll == child) {
3380 mForcedScroll = null;
3381 }
3382 }
3383
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003384 public void requestDisallowLongPress() {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003385 cancelLongPress();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003386 }
3387
Adrian Roosfa139752016-04-27 09:59:08 -07003388 public void requestDisallowDismiss() {
3389 mDisallowDismissInThisMotion = true;
3390 }
3391
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003392 public void cancelLongPress() {
3393 mSwipeHelper.cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02003394 }
3395
Selim Cinekfab078b2014-03-27 22:45:58 +01003396 @Override
3397 public boolean isScrolledToTop() {
3398 return mOwnScrollY == 0;
3399 }
3400
3401 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003402 public boolean isScrolledToBottom() {
3403 return mOwnScrollY >= getScrollRange();
3404 }
3405
3406 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003407 public View getHostView() {
3408 return this;
3409 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003410
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003411 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003412 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003413 }
3414
Mady Mellorc2dbe492017-03-30 13:22:03 -07003415 public void checkSnoozeLeavebehind() {
3416 if (mCheckForLeavebehind) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09003417 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
3418 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3419 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003420 mCheckForLeavebehind = false;
3421 }
3422 }
3423
3424 public void resetCheckSnoozeLeavebehind() {
3425 mCheckForLeavebehind = true;
3426 }
3427
Selim Cinek1685e632014-04-08 02:27:49 +02003428 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003429 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003430 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003431 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02003432 }
3433
3434 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003435 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07003436 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08003437 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003438 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003439 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003440 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003441 clearTemporaryViews();
3442 clearUserLockedViews();
3443 }
3444 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01003445
Selim Cinek5b1591a2017-07-03 17:05:01 +02003446 private void clearUserLockedViews() {
3447 for (int i = 0; i < getChildCount(); i++) {
3448 ExpandableView child = (ExpandableView) getChildAt(i);
3449 if (child instanceof ExpandableNotificationRow) {
3450 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3451 row.setUserLocked(false);
3452 }
3453 }
3454 }
3455
3456 private void clearTemporaryViews() {
3457 // lets make sure nothing is in the overlay / transient anymore
3458 clearTemporaryViews(this);
3459 for (int i = 0; i < getChildCount(); i++) {
3460 ExpandableView child = (ExpandableView) getChildAt(i);
3461 if (child instanceof ExpandableNotificationRow) {
3462 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3463 clearTemporaryViews(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003464 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003465 }
Selim Cinek1685e632014-04-08 02:27:49 +02003466 }
3467
Selim Cineka5703182016-05-11 21:23:16 -04003468 private void clearTemporaryViews(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003469 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003470 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003471 }
Selim Cineka5703182016-05-11 21:23:16 -04003472 if (viewGroup != null) {
3473 viewGroup.getOverlay().clear();
3474 }
Selim Cinekd1395642016-04-28 12:22:42 -07003475 }
3476
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003477 public void onPanelTrackingStarted() {
3478 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003479 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003480 }
3481 public void onPanelTrackingStopped() {
3482 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003483 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003484 }
3485
Selim Cinekb24e0a92015-06-09 20:17:30 -07003486 public void resetScrollPosition() {
3487 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003488 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003489 }
3490
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003491 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003492 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003493 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003494 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003495 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003496 if (!mIsExpanded) {
3497 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003498 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08003499 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003500 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003501 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003502 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003503 }
3504 }
3505
3506 private void updateChronometers() {
3507 int childCount = getChildCount();
3508 for (int i = 0; i < childCount; i++) {
3509 updateChronometerForChild(getChildAt(i));
3510 }
3511 }
3512
3513 private void updateChronometerForChild(View child) {
3514 if (child instanceof ExpandableNotificationRow) {
3515 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3516 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003517 }
Selim Cinek1685e632014-04-08 02:27:49 +02003518 }
3519
Jorim Jaggibe565df2014-04-28 17:51:23 +02003520 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003521 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003522 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003523 updateScrollPositionOnExpandInBottom(view);
3524 clampScrollPosition();
Selim Cinekaef92ef2014-06-06 18:06:04 +02003525 notifyHeightChangeListener(view);
Selim Cinekbc243a92016-09-27 16:35:13 -07003526 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3527 ? (ExpandableNotificationRow) view
3528 : null;
3529 if (row != null && (row == mFirstVisibleBackgroundChild
3530 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3531 updateAlgorithmLayoutMinHeight();
3532 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003533 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003534 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003535 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003536 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003537 }
3538
Selim Cineka5e211b2014-08-11 17:35:48 +02003539 @Override
3540 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003541 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003542 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003543 }
3544
Selim Cinekf7a14c02014-07-07 14:01:46 +02003545 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07003546 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02003547 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003548 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003549 if (row.isSummaryWithChildren()) {
3550 return;
3551 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003552 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003553 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003554 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003555 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003556 }
Selim Cinekdb167372016-11-17 15:41:17 -08003557 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Anthony Chen9e05d462017-04-07 10:10:21 -07003558 if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08003559 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3560 }
3561 if (endPosition > layoutEnd) {
3562 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003563 mDisallowScrollingInThisMotion = true;
3564 }
3565 }
3566 }
3567 }
3568
Jorim Jaggibe565df2014-04-28 17:51:23 +02003569 public void setOnHeightChangedListener(
3570 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3571 this.mOnHeightChangedListener = mOnHeightChangedListener;
3572 }
3573
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003574 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3575 mOnEmptySpaceClickListener = listener;
3576 }
3577
Selim Cinek572bbd42014-04-25 16:43:27 +02003578 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003579 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003580 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003581 runAnimationFinishedRunnables();
Selim Cinek0fccc722015-07-29 17:04:36 -07003582 clearViewOverlays();
Selim Cinek8fc78752016-07-13 14:34:56 -07003583 clearHeadsUpDisappearRunning();
3584 }
3585
3586 private void clearHeadsUpDisappearRunning() {
3587 for (int i = 0; i < getChildCount(); i++) {
3588 View view = getChildAt(i);
3589 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003590 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003591 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003592 if (row.isSummaryWithChildren()) {
3593 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003594 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003595 }
3596 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003597 }
3598 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003599 }
3600
3601 private void clearViewOverlays() {
3602 for (View view : mClearOverlayViewsWhenFinished) {
Selim Cineka5703182016-05-11 21:23:16 -04003603 StackStateAnimator.removeFromOverlay(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003604 }
Selim Cinekc8df0642017-02-23 17:01:04 -08003605 mClearOverlayViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003606 }
3607
3608 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003609 for (Runnable runnable : mAnimationFinishedRunnables) {
3610 runnable.run();
3611 }
3612 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003613 }
3614
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003615 /**
3616 * See {@link AmbientState#setDimmed}.
3617 */
3618 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07003619 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003620 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003621 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003622 mDimmedNeedsAnimation = true;
3623 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003624 animateDimmed(dimmed);
3625 } else {
3626 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003627 }
3628 requestChildrenUpdate();
3629 }
3630
Selim Cinek8a9308d2017-08-24 09:31:08 -07003631 @VisibleForTesting
3632 boolean isDimmed() {
3633 return mAmbientState.isDimmed();
3634 }
3635
Selim Cinekd35c2792016-01-21 13:20:57 -08003636 private void setDimAmount(float dimAmount) {
3637 mDimAmount = dimAmount;
3638 updateBackgroundDimming();
3639 }
3640
3641 private void animateDimmed(boolean dimmed) {
3642 if (mDimAnimator != null) {
3643 mDimAnimator.cancel();
3644 }
3645 float target = dimmed ? 1.0f : 0.0f;
3646 if (target == mDimAmount) {
3647 return;
3648 }
3649 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3650 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3651 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3652 mDimAnimator.addListener(mDimEndListener);
3653 mDimAnimator.addUpdateListener(mDimUpdateListener);
3654 mDimAnimator.start();
3655 }
3656
Jorim Jaggiae441282014-08-01 02:45:18 +02003657 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3658 if (hideSensitive != mAmbientState.isHideSensitive()) {
3659 int childCount = getChildCount();
3660 for (int i = 0; i < childCount; i++) {
3661 ExpandableView v = (ExpandableView) getChildAt(i);
3662 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3663 }
3664 mAmbientState.setHideSensitive(hideSensitive);
3665 if (animate && mAnimationsEnabled) {
3666 mHideSensitiveNeedsAnimation = true;
3667 mNeedsAnimation = true;
3668 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08003669 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02003670 requestChildrenUpdate();
3671 }
3672 }
3673
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003674 /**
3675 * See {@link AmbientState#setActivatedChild}.
3676 */
Selim Cineka32ab602014-06-11 15:06:01 +02003677 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003678 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003679 if (mAnimationsEnabled) {
3680 mActivateNeedsAnimation = true;
3681 mNeedsAnimation = true;
3682 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003683 requestChildrenUpdate();
3684 }
3685
Selim Cineka32ab602014-06-11 15:06:01 +02003686 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003687 return mAmbientState.getActivatedChild();
3688 }
3689
Selim Cinek572bbd42014-04-25 16:43:27 +02003690 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003691 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003692 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003693 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02003694 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003695 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003696 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003697 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003698 updateViewShadows();
3699 }
3700
3701 private void updateViewShadows() {
3702 // we need to work around an issue where the shadow would not cast between siblings when
3703 // their z difference is between 0 and 0.1
3704
3705 // Lefts first sort by Z difference
3706 for (int i = 0; i < getChildCount(); i++) {
3707 ExpandableView child = (ExpandableView) getChildAt(i);
3708 if (child.getVisibility() != GONE) {
3709 mTmpSortedChildren.add(child);
3710 }
3711 }
3712 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3713
3714 // Now lets update the shadow for the views
3715 ExpandableView previous = null;
3716 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3717 ExpandableView expandableView = mTmpSortedChildren.get(i);
3718 float translationZ = expandableView.getTranslationZ();
3719 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3720 float diff = otherZ - translationZ;
3721 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3722 // There is no fake shadow to be drawn
3723 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3724 } else {
3725 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003726 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3727 expandableView.setFakeShadowIntensity(
3728 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003729 previous.getOutlineAlpha(), (int) yLocation,
3730 previous.getOutlineTranslation());
3731 }
3732 previous = expandableView;
3733 }
3734
3735 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003736 }
3737
Lucas Dupine17ce522017-07-17 15:45:06 -07003738 /**
3739 * Update colors of "dismiss" and "empty shade" views.
3740 *
3741 * @param lightTheme True if light theme should be used.
3742 */
3743 public void updateDecorViews(boolean lightTheme) {
3744 if (lightTheme == mUsingLightTheme) {
3745 return;
3746 }
3747 mUsingLightTheme = lightTheme;
3748 Context context = new ContextThemeWrapper(mContext,
3749 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
3750 final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
3751 mDismissView.setTextColor(textColor);
3752 mEmptyShadeView.setTextColor(textColor);
3753 }
3754
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003755 public void goToFullShade(long delay) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003756 if (mDismissView != null) {
3757 mDismissView.setInvisible();
3758 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003759 mEmptyShadeView.setInvisible();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003760 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003761 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003762 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003763 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003764 }
3765
Selim Cinek1408eb52014-06-02 14:45:38 +02003766 public void cancelExpandHelper() {
3767 mExpandHelper.cancel();
3768 }
3769
3770 public void setIntrinsicPadding(int intrinsicPadding) {
3771 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07003772 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02003773 }
3774
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003775 public int getIntrinsicPadding() {
3776 return mIntrinsicPadding;
3777 }
3778
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003779 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003780 * @return the y position of the first notification
3781 */
3782 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003783 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003784 }
3785
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003786 @Override
3787 public boolean shouldDelayChildPressedState() {
3788 return true;
3789 }
3790
Jorim Jaggi457cc352014-06-02 22:47:42 +02003791 /**
John Spurlockbf370992014-06-17 13:58:31 -04003792 * See {@link AmbientState#setDark}.
3793 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003794 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02003795 if (mAmbientState.isDark() == dark) {
3796 return;
3797 }
John Spurlockbf370992014-06-17 13:58:31 -04003798 mAmbientState.setDark(dark);
3799 if (animate && mAnimationsEnabled) {
3800 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003801 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
John Spurlockbf370992014-06-17 13:58:31 -04003802 mNeedsAnimation = true;
Selim Cinek972123d2016-05-03 14:25:58 -07003803 setBackgroundFadeAmount(0.0f);
3804 } else if (!dark) {
3805 setBackgroundFadeAmount(1.0f);
John Spurlockbf370992014-06-17 13:58:31 -04003806 }
3807 requestChildrenUpdate();
Selim Cinek6811d722016-01-19 17:53:12 -08003808 if (dark) {
Selim Cinek6811d722016-01-19 17:53:12 -08003809 mScrimController.setExcludedBackgroundArea(null);
3810 } else {
3811 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08003812 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003813
3814 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003815 updateContentHeight();
3816 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04003817 }
3818
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003819 /**
3820 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
3821 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
3822 * {@link #mAmbientState}'s dark mode is toggled.
3823 */
3824 private void updateWillNotDraw() {
Adrian Roosf0b4f962017-05-25 11:53:11 -07003825 boolean willDraw = !mAmbientState.isDark() && mShouldDrawNotificationBackground || DEBUG;
3826 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003827 }
3828
Selim Cinek972123d2016-05-03 14:25:58 -07003829 private void setBackgroundFadeAmount(float fadeAmount) {
3830 mBackgroundFadeAmount = fadeAmount;
3831 updateBackgroundDimming();
3832 }
3833
3834 public float getBackgroundFadeAmount() {
3835 return mBackgroundFadeAmount;
3836 }
3837
3838 private void startBackgroundFadeIn() {
3839 ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, BACKGROUND_FADE, 0f, 1f);
Adrian Roos28f90c72017-05-08 17:24:26 -07003840 fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
Selim Cinek972123d2016-05-03 14:25:58 -07003841 fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
3842 fadeAnimator.start();
3843 }
3844
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003845 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07003846 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003847 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3848 }
3849 if (screenLocation.y > getBottomMostNotificationBottom()) {
3850 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
3851 }
3852 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
3853 if (child != null) {
3854 return getNotGoneIndex(child);
3855 } else {
3856 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3857 }
3858 }
3859
3860 private int getNotGoneIndex(View child) {
3861 int count = getChildCount();
3862 int notGoneIndex = 0;
3863 for (int i = 0; i < count; i++) {
3864 View v = getChildAt(i);
3865 if (child == v) {
3866 return notGoneIndex;
3867 }
3868 if (v.getVisibility() != View.GONE) {
3869 notGoneIndex++;
3870 }
3871 }
3872 return -1;
3873 }
3874
Anthony Chen5e3742e2017-04-07 14:28:44 -07003875 public void setDismissView(@NonNull DismissView dismissView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003876 int index = -1;
3877 if (mDismissView != null) {
3878 index = indexOfChild(mDismissView);
3879 removeView(mDismissView);
3880 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003881 mDismissView = dismissView;
Selim Cinek01af3342016-02-09 19:25:31 -08003882 addView(mDismissView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003883 }
3884
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003885 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003886 int index = -1;
3887 if (mEmptyShadeView != null) {
3888 index = indexOfChild(mEmptyShadeView);
3889 removeView(mEmptyShadeView);
3890 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003891 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08003892 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003893 }
3894
3895 public void updateEmptyShadeView(boolean visible) {
3896 int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
3897 int newVisibility = visible ? VISIBLE : GONE;
3898 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003899 if (newVisibility != GONE) {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003900 if (mEmptyShadeView.willBeGone()) {
3901 mEmptyShadeView.cancelAnimation();
3902 } else {
3903 mEmptyShadeView.setInvisible();
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003904 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003905 mEmptyShadeView.setVisibility(newVisibility);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003906 mEmptyShadeView.setWillBeGone(false);
3907 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003908 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003909 } else {
Selim Cinek20867102014-12-10 17:09:17 +01003910 Runnable onFinishedRunnable = new Runnable() {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003911 @Override
3912 public void run() {
3913 mEmptyShadeView.setVisibility(GONE);
3914 mEmptyShadeView.setWillBeGone(false);
3915 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003916 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003917 }
Selim Cinek20867102014-12-10 17:09:17 +01003918 };
Selim Cineka9d3efd2016-08-16 14:16:59 -07003919 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek20867102014-12-10 17:09:17 +01003920 mEmptyShadeView.setWillBeGone(true);
3921 mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
3922 } else {
3923 mEmptyShadeView.setInvisible();
3924 onFinishedRunnable.run();
3925 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003926 }
3927 }
3928 }
3929
Dan Sandlereceda3d2014-07-21 15:35:01 -04003930 public void updateDismissView(boolean visible) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003931 if (mDismissView == null) {
3932 return;
3933 }
3934
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003935 int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003936 int newVisibility = visible ? VISIBLE : GONE;
3937 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003938 if (newVisibility != GONE) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003939 if (mDismissView.willBeGone()) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003940 mDismissView.cancelAnimation();
3941 } else {
3942 mDismissView.setInvisible();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003943 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003944 mDismissView.setVisibility(newVisibility);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003945 mDismissView.setWillBeGone(false);
3946 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02003947 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003948 } else {
Selim Cinek7d5f3742014-11-07 18:07:49 +01003949 Runnable dimissHideFinishRunnable = new Runnable() {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003950 @Override
3951 public void run() {
3952 mDismissView.setVisibility(GONE);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003953 mDismissView.setWillBeGone(false);
3954 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02003955 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003956 }
Selim Cinek7d5f3742014-11-07 18:07:49 +01003957 };
Selim Cinek20867102014-12-10 17:09:17 +01003958 if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
Selim Cinek7d5f3742014-11-07 18:07:49 +01003959 mDismissView.setWillBeGone(true);
3960 mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
3961 } else {
3962 dimissHideFinishRunnable.run();
Selim Cinek7d5f3742014-11-07 18:07:49 +01003963 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003964 }
3965 }
3966 }
3967
3968 public void setDismissAllInProgress(boolean dismissAllInProgress) {
3969 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07003970 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07003971 handleDismissAllClipping();
3972 }
3973
3974 private void handleDismissAllClipping() {
3975 final int count = getChildCount();
3976 boolean previousChildWillBeDismissed = false;
3977 for (int i = 0; i < count; i++) {
3978 ExpandableView child = (ExpandableView) getChildAt(i);
3979 if (child.getVisibility() == GONE) {
3980 continue;
3981 }
3982 if (mDismissAllInProgress && previousChildWillBeDismissed) {
3983 child.setMinClipTopAmount(child.getClipTopAmount());
3984 } else {
3985 child.setMinClipTopAmount(0);
3986 }
3987 previousChildWillBeDismissed = canChildBeDismissed(child);
3988 }
Selim Cineka272dfe2015-02-20 18:12:28 +01003989 }
3990
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003991 public boolean isDismissViewNotGone() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003992 return mDismissView != null
3993 && mDismissView.getVisibility() != View.GONE
3994 && !mDismissView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003995 }
3996
3997 public boolean isDismissViewVisible() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003998 return mDismissView != null && mDismissView.isVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003999 }
4000
4001 public int getDismissViewHeight() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004002 return mDismissView == null ? 0 : mDismissView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004003 }
4004
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004005 public int getEmptyShadeViewHeight() {
4006 return mEmptyShadeView.getHeight();
4007 }
4008
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004009 public float getBottomMostNotificationBottom() {
4010 final int count = getChildCount();
4011 float max = 0;
4012 for (int childIdx = 0; childIdx < count; childIdx++) {
4013 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4014 if (child.getVisibility() == GONE) {
4015 continue;
4016 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004017 float bottom = child.getTranslationY() + child.getActualHeight()
4018 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004019 if (bottom > max) {
4020 max = bottom;
4021 }
4022 }
Selim Cinekd2281152015-04-10 14:37:46 -07004023 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004024 }
4025
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004026 public void setStatusBar(StatusBar statusBar) {
4027 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004028 }
4029
Selim Cinekb5605e52015-02-20 18:21:41 +01004030 public void setGroupManager(NotificationGroupManager groupManager) {
4031 this.mGroupManager = groupManager;
4032 }
4033
Selim Cinekd9acca52014-09-01 22:33:25 +02004034 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01004035 requestAnimateEverything();
4036 }
4037
4038 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004039 if (mIsExpanded && mAnimationsEnabled) {
4040 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004041 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004042 requestChildrenUpdate();
4043 }
4044 }
4045
Selim Cinek04fb2582015-06-02 19:58:09 +02004046 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004047 int childCount = getChildCount();
4048 for (int i = childCount - 1; i >= 0; i--) {
4049 ExpandableView child = (ExpandableView) getChildAt(i);
4050 if (child.getVisibility() != View.GONE) {
4051 float childTop = child.getY();
4052 if (childTop > touchY) {
4053 // we are above a notification entirely let's abort
4054 return false;
4055 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004056 boolean belowChild = touchY > childTop + child.getActualHeight()
4057 - child.getClipBottomAmount();
Selim Cinekabf60bb2015-02-20 17:36:10 +01004058 if (child == mDismissView) {
4059 if(!belowChild && !mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
4060 touchY - childTop)) {
4061 // We clicked on the dismiss button
4062 return false;
4063 }
4064 } else if (child == mEmptyShadeView) {
4065 // We arrived at the empty shade view, for which we accept all clicks
4066 return true;
4067 } else if (!belowChild){
4068 // We are on a child
4069 return false;
4070 }
4071 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004072 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004073 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004074 }
4075
Selim Cinekb5605e52015-02-20 18:21:41 +01004076 @Override
4077 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004078 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4079 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004080 if (animated) {
4081 mExpandedGroupView = changedRow;
4082 mNeedsAnimation = true;
4083 }
4084 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004085 if (!mGroupExpandedForMeasure) {
4086 onHeightChanged(changedRow, false /* needsAnimation */);
4087 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004088 runAfterAnimationFinished(new Runnable() {
4089 @Override
4090 public void run() {
4091 changedRow.onFinishedExpansionChange();
4092 }
4093 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004094 }
4095
4096 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004097 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004098 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004099 }
4100
Selim Cinekc22fff62016-05-20 12:44:30 -07004101 /** @hide */
4102 @Override
4103 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4104 super.onInitializeAccessibilityEventInternal(event);
4105 event.setScrollable(mScrollable);
4106 event.setScrollX(mScrollX);
4107 event.setScrollY(mOwnScrollY);
4108 event.setMaxScrollX(mScrollX);
4109 event.setMaxScrollY(getScrollRange());
4110 }
4111
4112 @Override
4113 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4114 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004115 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004116 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004117 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004118 info.addAction(
4119 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4120 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4121 }
Selim Cinekef406062016-09-29 17:33:13 -07004122 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004123 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4124 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4125 }
4126 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004127 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4128 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004129 }
4130
4131 /** @hide */
4132 @Override
4133 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4134 if (super.performAccessibilityActionInternal(action, arguments)) {
4135 return true;
4136 }
4137 if (!isEnabled()) {
4138 return false;
4139 }
4140 int direction = -1;
4141 switch (action) {
4142 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4143 // fall through
4144 case android.R.id.accessibilityActionScrollDown:
4145 direction = 1;
4146 // fall through
4147 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4148 // fall through
4149 case android.R.id.accessibilityActionScrollUp:
4150 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004151 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004152 final int targetScrollY = Math.max(0,
4153 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4154 if (targetScrollY != mOwnScrollY) {
4155 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004156 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004157 return true;
4158 }
4159 break;
4160 }
4161 return false;
4162 }
4163
Selim Cinekef5127e2015-12-21 16:55:58 -08004164 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004165 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004166 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004167 }
4168
4169 public void generateChildOrderChangedEvent() {
4170 if (mIsExpanded && mAnimationsEnabled) {
4171 mGenerateChildOrderChangedEvent = true;
4172 mNeedsAnimation = true;
4173 requestChildrenUpdate();
4174 }
4175 }
4176
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004177 @Override
4178 public int getContainerChildCount() {
4179 return getChildCount();
4180 }
4181
4182 @Override
4183 public View getContainerChildAt(int i) {
4184 return getChildAt(i);
4185 }
4186
4187 @Override
4188 public void removeContainerView(View v) {
4189 removeView(v);
4190 }
4191
4192 @Override
4193 public void addContainerView(View v) {
4194 addView(v);
4195 }
4196
Selim Cinek684a4422015-04-15 16:18:39 -07004197 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004198 mAnimationFinishedRunnables.add(runnable);
4199 }
4200
4201 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
4202 mHeadsUpManager = headsUpManager;
4203 mAmbientState.setHeadsUpManager(headsUpManager);
4204 }
4205
4206 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004207 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004208 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4209 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004210 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004211 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004212 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004213 requestChildrenUpdate();
4214 }
4215 }
4216
4217 public void setShadeExpanded(boolean shadeExpanded) {
4218 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004219 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004220 }
4221
Selim Cineka59ecc32015-04-07 10:51:49 -07004222 /**
4223 * Set the boundary for the bottom heads up position. The heads up will always be above this
4224 * position.
4225 *
4226 * @param height the height of the screen
4227 * @param bottomBarHeight the height of the bar on the bottom
4228 */
4229 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4230 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4231 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004232 requestChildrenUpdate();
4233 }
4234
4235 public void setTrackingHeadsUp(boolean trackingHeadsUp) {
4236 mTrackingHeadsUp = trackingHeadsUp;
4237 }
4238
Selim Cinekaac93252015-04-14 20:04:12 -07004239 public void setScrimController(ScrimController scrimController) {
4240 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004241 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004242 }
4243
Selim Cinekbbc580b2015-06-03 14:11:03 +02004244 public void forceNoOverlappingRendering(boolean force) {
4245 mForceNoOverlappingRendering = force;
4246 }
4247
4248 @Override
4249 public boolean hasOverlappingRendering() {
4250 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4251 }
4252
Selim Cinek6811d722016-01-19 17:53:12 -08004253 public void setAnimationRunning(boolean animationRunning) {
4254 if (animationRunning != mAnimationRunning) {
4255 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004256 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004257 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004258 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004259 }
4260 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004261 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004262 }
4263 }
4264
Selim Cinek3776fe02016-02-04 13:32:43 -08004265 public boolean isExpanded() {
4266 return mIsExpanded;
4267 }
4268
Adrian Roos7d062c42017-03-30 15:11:43 -07004269 public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
4270 if (mPulsing == null && pulsing == null) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004271 return;
4272 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004273 mPulsing = pulsing;
Selim Cinekebf42342017-07-13 15:46:10 +02004274 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004275 updateNotificationAnimationStates();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004276 updateContentHeight();
4277 notifyHeightChangeListener(mShelf);
Adrian Roosd83e9992017-03-16 15:17:57 -07004278 requestChildrenUpdate();
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004279 }
4280
Selim Cinek07304f5222016-05-19 18:31:36 -07004281 public void setFadingOut(boolean fadingOut) {
4282 if (fadingOut != mFadingOut) {
4283 mFadingOut = fadingOut;
4284 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004285 }
4286 }
4287
Selim Cinek48ff9b42016-11-09 19:31:51 -08004288 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4289 if (mScrimController == null) {
4290 // we're not set up yet.
4291 return;
4292 }
4293 if (parentNotFullyVisible != mParentNotFullyVisible) {
4294 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004295 updateFadingState();
4296 }
4297 }
4298
4299 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004300 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004301 updateSrcDrawing();
4302 }
4303
Selim Cinek31d37b92016-04-26 09:56:42 -07004304 @Override
4305 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4306 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004307 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004308 }
4309
Selim Cinekbc243a92016-09-27 16:35:13 -07004310 public void setQsExpanded(boolean qsExpanded) {
4311 mQsExpanded = qsExpanded;
4312 updateAlgorithmLayoutMinHeight();
4313 }
4314
Selim Cinekef406062016-09-29 17:33:13 -07004315 public void setOwnScrollY(int ownScrollY) {
4316 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004317 // We still want to call the normal scrolled changed for accessibility reasons
4318 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004319 mOwnScrollY = ownScrollY;
4320 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004321 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004322 }
4323 }
4324
Selim Cinek281c2022016-10-13 19:14:43 -07004325 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004326 int index = -1;
4327 if (mShelf != null) {
4328 index = indexOfChild(mShelf);
4329 removeView(mShelf);
4330 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004331 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004332 addView(mShelf, index);
4333 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004334 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004335 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004336 }
4337
4338 public NotificationShelf getNotificationShelf() {
4339 return mShelf;
4340 }
4341
Selim Cinekad7fac02016-10-18 17:09:15 -07004342 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4343 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4344 mMaxDisplayedNotifications = maxDisplayedNotifications;
4345 updateContentHeight();
4346 notifyHeightChangeListener(mShelf);
4347 }
4348 }
4349
Selim Cinek48ff9b42016-11-09 19:31:51 -08004350 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004351 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004352 }
4353
Selim Cinekcafa87f2016-10-26 17:00:17 -07004354 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4355 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4356 updateClipping();
4357 }
4358
4359 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4360 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4361 updateClipping();
4362 }
4363
Selim Cinek355652a2016-12-07 13:32:12 -08004364 public void setStatusBarState(int statusBarState) {
4365 mStatusBarState = statusBarState;
4366 mAmbientState.setStatusBarState(statusBarState);
Selim Cinekd9b7dd42017-11-10 17:53:47 -08004367 applyRoundedNess();
Selim Cinek515b2032017-11-15 10:20:19 -08004368 updateSidePaddings();
4369 }
4370
4371 private void updateSidePaddings() {
4372 int sidePaddings = mStatusBarState == StatusBarState.KEYGUARD ? mLockscreenSidePaddings : 0;
4373 if (sidePaddings != mSidePaddings) {
4374 boolean animate = isShown();
4375 mSidePaddings = sidePaddings;
4376 PropertyAnimator.setProperty(this, SIDE_PADDINGS, sidePaddings,
4377 SIDE_PADDING_PROPERTIES, animate);
4378 }
4379 }
4380
4381 protected void setCurrentSidePadding(float sidePadding) {
4382 mCurrentSidePadding = sidePadding;
4383 updateBackground();
4384 applySidePaddingsToChildren();
4385 }
4386
4387 private void applySidePaddingsToChildren() {
4388 for (int i = 0; i < getChildCount(); i++) {
4389 View view = getChildAt(i);
4390 updateCurrentSidePaddings(view);
4391 }
4392 }
4393
4394 private void updateCurrentSidePaddings(View view) {
4395 if (!(view instanceof ExpandableOutlineView)) {
4396 return;
4397 }
4398 ExpandableOutlineView outlineView = (ExpandableOutlineView) view;
4399 outlineView.setCurrentSidePaddings(mCurrentSidePadding);
4400 }
4401
4402 protected float getCurrentSidePadding() {
4403 return mCurrentSidePadding;
Selim Cinek355652a2016-12-07 13:32:12 -08004404 }
4405
Selim Cinekd5ab6452016-12-08 16:34:00 -08004406 public void setExpandingVelocity(float expandingVelocity) {
4407 mAmbientState.setExpandingVelocity(expandingVelocity);
4408 }
4409
Selim Cinekfcff4c62016-12-27 14:26:06 +01004410 public float getOpeningHeight() {
4411 if (mEmptyShadeView.getVisibility() == GONE) {
4412 return getMinExpansionHeight();
4413 } else {
4414 return getAppearEndPosition();
4415 }
4416 }
4417
4418 public void setIsFullWidth(boolean isFullWidth) {
4419 mAmbientState.setPanelFullWidth(isFullWidth);
4420 }
4421
Selim Cinekec29d342017-05-05 18:31:49 -07004422 public void setUnlockHintRunning(boolean running) {
4423 mAmbientState.setUnlockHintRunning(running);
4424 }
4425
Selim Cinek5cf1d052017-06-01 17:36:46 -07004426 public void setQsCustomizerShowing(boolean isShowing) {
4427 mAmbientState.setQsCustomizerShowing(isShowing);
4428 requestChildrenUpdate();
4429 }
4430
4431 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4432 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4433 }
4434
Adrian Roosdc747bd2017-06-01 16:09:15 -07004435 public void setDarkShelfOffsetX(int shelfOffsetX) {
4436 mShelf.setDarkOffsetX(shelfOffsetX);
4437 }
4438
Selim Cinek707e2072017-06-30 18:32:40 +02004439 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4440 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
4441 + " alpha:%f scrollY:%d]",
4442 this.getClass().getSimpleName(),
4443 mPulsing != null ?"T":"f",
4444 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4445 getVisibility() == View.VISIBLE ? "visible"
4446 : getVisibility() == View.GONE ? "gone"
4447 : "invisible",
4448 getAlpha(),
4449 mAmbientState.getScrollY()));
4450 }
4451
Selim Cinek7103fd42016-05-09 22:22:33 -04004452 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004453 * A listener that is notified when the empty space below the notifications is clicked on
4454 */
4455 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004456 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004457 }
4458
4459 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004460 * A listener that gets notified when the overscroll at the top has changed.
4461 */
4462 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004463
4464 /**
4465 * Notifies a listener that the overscroll has changed.
4466 *
4467 * @param amount the amount of overscroll, in pixels
4468 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4469 * unrubberbanded motion to directly expand overscroll view (e.g expand
4470 * QS)
4471 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004472 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004473
4474 /**
4475 * Notify a listener that the scroller wants to escape from the scrolling motion and
4476 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4477 *
4478 * @param velocity The velocity that the Scroller had when over flinging
4479 * @param open Should the fling open or close the overscroll view.
4480 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004481 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004482 }
4483
Mady Mellor95d743c2017-01-10 12:05:27 -08004484 private class NotificationSwipeHelper extends SwipeHelper
4485 implements NotificationSwipeActionHelper {
4486 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004487 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004488 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004489
4490 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4491 super(swipeDirection, callback, context);
4492 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004493 mFalsingCheck = new Runnable() {
4494 @Override
4495 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004496 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004497 }
4498 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004499 }
4500
4501 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004502 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004503 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004504 if (mCurrMenuRow != null) {
4505 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004506 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004507 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004508 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004509
Mady Mellor95d743c2017-01-10 12:05:27 -08004510 // Slide back any notifications that might be showing a menu
4511 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004512
4513 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004514 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
4515 mCurrMenuRow = row.createMenu();
4516 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4517 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor89e15ec2017-06-28 17:08:21 -07004518 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004519 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004520 }
4521
4522 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004523 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004524 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004525 if (mCurrMenuRow != null) {
4526 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004527 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004528 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004529
Mady Mellor95d743c2017-01-10 12:05:27 -08004530 @Override
4531 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4532 float translation) {
4533 if (mCurrMenuRow != null) {
4534 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004535 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004536 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004537 }
4538
4539 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004540 public void dismissChild(final View view, float velocity,
4541 boolean useAccelerateInterpolator) {
4542 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004543 if (mIsExpanded) {
4544 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4545 // of the panel early.
4546 handleChildDismissed(view);
4547 }
Eliot Courtney47098cb2017-10-18 17:30:30 +09004548 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
4549 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4550 false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004551 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004552 }
4553
4554 @Override
4555 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004556 super.snapChild(animView, targetLeft, velocity);
4557 onDragCancelled(animView);
4558 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004559 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004560 }
4561 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004562
4563 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004564 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4565 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004566 }
4567
Mady Mellorbd707492017-05-10 17:51:25 -07004568 public boolean isFalseGesture(MotionEvent ev) {
4569 return super.isFalseGesture(ev);
4570 }
4571
Mady Mellor95d743c2017-01-10 12:05:27 -08004572 private void handleMenuCoveredOrDismissed() {
4573 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4574 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004575 }
4576 }
4577
Mady Mellor4b80b102016-01-22 08:03:58 -08004578 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004579 public Animator getViewTranslationAnimator(View v, float target,
4580 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004581 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004582 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4583 } else {
4584 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004585 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004586 }
4587
4588 @Override
4589 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004590 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004591 }
4592
4593 @Override
4594 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004595 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004596 }
4597
Mady Mellor95d743c2017-01-10 12:05:27 -08004598 @Override
4599 public void dismiss(View animView, float velocity) {
4600 dismissChild(animView, velocity,
4601 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4602 }
4603
4604 @Override
4605 public void snap(View animView, float targetLeft, float velocity) {
4606 snapChild(animView, targetLeft, velocity);
4607 }
4608
4609 @Override
4610 public boolean swipedFarEnough(float translation, float viewSize) {
4611 return swipedFarEnough();
4612 }
4613
4614 @Override
4615 public boolean swipedFastEnough(float translation, float velocity) {
4616 return swipedFastEnough();
4617 }
4618
4619 @Override
4620 public float getMinDismissVelocity() {
4621 return getEscapeVelocity();
4622 }
4623
4624 public void onMenuShown(View animView) {
4625 onDragCancelled(animView);
4626
4627 // If we're on the lockscreen we want to false this.
4628 if (isAntiFalsingNeeded()) {
4629 mHandler.removeCallbacks(mFalsingCheck);
4630 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4631 }
4632 }
4633
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004634 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09004635 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004636 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004637 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4638 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004639 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004640 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004641 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004642 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004643 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004644 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004645 if (view != null && !isTouchInView(ev, view)) {
4646 // Touch was outside visible guts / menu notification, close what's visible
Eliot Courtney47098cb2017-10-18 17:30:30 +09004647 mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
4648 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4649 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004650 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004651 }
4652 }
4653
Mady Mellor95d743c2017-01-10 12:05:27 -08004654 public void resetExposedMenuView(boolean animate, boolean force) {
4655 if (mMenuExposedView == null
4656 || (!force && mMenuExposedView == mTranslatingParentView)) {
4657 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004658 return;
4659 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004660 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004661 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004662 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004663 0 /* leftTarget */, null /* updateListener */);
4664 if (anim != null) {
4665 anim.start();
4666 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004667 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
Selim Cinekb2e0f332017-08-18 12:24:38 -07004668 ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
4669 if (!row.isRemoved()) {
4670 row.resetTranslation();
4671 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004672 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004673 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004674 }
4675 }
4676
Mady Mellorc2dbe492017-03-30 13:22:03 -07004677 private boolean isTouchInView(MotionEvent ev, View view) {
4678 if (view == null) {
4679 return false;
4680 }
4681 final int height = (view instanceof ExpandableView)
4682 ? ((ExpandableView) view).getActualHeight()
4683 : view.getHeight();
4684 final int rx = (int) ev.getRawX();
4685 final int ry = (int) ev.getRawY();
4686 view.getLocationOnScreen(mTempInt2);
4687 final int x = mTempInt2[0];
4688 final int y = mTempInt2[1];
4689 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4690 boolean ret = rect.contains(rx, ry);
4691 return ret;
4692 }
4693
Selim Cinek33223572016-02-19 19:32:22 -08004694 private void updateContinuousShadowDrawing() {
4695 boolean continuousShadowUpdate = mAnimationRunning
4696 || !mAmbientState.getDraggedViews().isEmpty();
4697 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4698 if (continuousShadowUpdate) {
4699 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4700 } else {
4701 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4702 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004703 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004704 }
4705 }
4706
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004707 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004708 public void resetExposedMenuView(boolean animate, boolean force) {
4709 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004710 }
4711
4712 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4713 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4714 }
4715
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004716 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004717
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004718 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4719
4720 // ANIMATION_TYPE_ADD
4721 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004722 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004723 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004724 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004725 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004726 .animateZ()
4727 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004728
4729 // ANIMATION_TYPE_REMOVE
4730 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004731 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004732 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004733 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004734 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004735 .animateZ()
4736 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004737
4738 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4739 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004740 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004741 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004742 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004743 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004744 .animateZ()
4745 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004746
4747 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4748 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004749 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004750 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004751 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004752 .animateY()
4753 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004754 .animateZ(),
4755
4756 // ANIMATION_TYPE_START_DRAG
4757 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004758 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004759
4760 // ANIMATION_TYPE_SNAP_BACK
4761 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004762 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004763 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004764
4765 // ANIMATION_TYPE_ACTIVATED_CHILD
4766 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004767 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004768
4769 // ANIMATION_TYPE_DIMMED
4770 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004771 .animateDimmed(),
4772
4773 // ANIMATION_TYPE_CHANGE_POSITION
4774 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004775 .animateAlpha() // maybe the children change positions
4776 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004777 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004778 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004779 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004780 .animateZ(),
4781
4782 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004783 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004784
4785 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4786 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004787 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004788 .animateHeight()
4789 .animateTopInset()
4790 .animateY()
4791 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004792 .animateZ()
4793 .hasDelays(),
4794
4795 // ANIMATION_TYPE_HIDE_SENSITIVE
4796 new AnimationFilter()
4797 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02004798
4799 // ANIMATION_TYPE_VIEW_RESIZE
4800 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004801 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02004802 .animateHeight()
4803 .animateTopInset()
4804 .animateY()
4805 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02004806
Selim Cinekb5605e52015-02-20 18:21:41 +01004807 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
4808 new AnimationFilter()
4809 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004810 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01004811 .animateHeight()
4812 .animateTopInset()
4813 .animateY()
4814 .animateZ(),
4815
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004816 // ANIMATION_TYPE_HEADS_UP_APPEAR
4817 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004818 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004819 .animateHeight()
4820 .animateTopInset()
4821 .animateY()
4822 .animateZ(),
4823
4824 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4825 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004826 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004827 .animateHeight()
4828 .animateTopInset()
4829 .animateY()
4830 .animateZ(),
4831
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004832 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4833 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004834 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004835 .animateHeight()
4836 .animateTopInset()
4837 .animateY()
4838 .animateZ()
4839 .hasDelays(),
4840
Selim Cineka59ecc32015-04-07 10:51:49 -07004841 // ANIMATION_TYPE_HEADS_UP_OTHER
4842 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004843 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07004844 .animateHeight()
4845 .animateTopInset()
4846 .animateY()
4847 .animateZ(),
4848
Selim Cinekd9acca52014-09-01 22:33:25 +02004849 // ANIMATION_TYPE_EVERYTHING
4850 new AnimationFilter()
4851 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004852 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02004853 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02004854 .animateDimmed()
4855 .animateHideSensitive()
4856 .animateHeight()
4857 .animateTopInset()
4858 .animateY()
4859 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004860 };
4861
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004862 static int[] LENGTHS = new int[] {
4863
4864 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004865 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004866
4867 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004868 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004869
4870 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4871 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4872
4873 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4874 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4875
4876 // ANIMATION_TYPE_START_DRAG
4877 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4878
4879 // ANIMATION_TYPE_SNAP_BACK
4880 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4881
4882 // ANIMATION_TYPE_ACTIVATED_CHILD
4883 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
4884
4885 // ANIMATION_TYPE_DIMMED
4886 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004887
4888 // ANIMATION_TYPE_CHANGE_POSITION
4889 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04004890
4891 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004892 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004893
4894 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4895 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02004896
4897 // ANIMATION_TYPE_HIDE_SENSITIVE
4898 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02004899
4900 // ANIMATION_TYPE_VIEW_RESIZE
4901 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02004902
Selim Cinekb5605e52015-02-20 18:21:41 +01004903 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08004904 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01004905
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004906 // ANIMATION_TYPE_HEADS_UP_APPEAR
4907 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
4908
4909 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4910 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4911
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004912 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4913 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4914
Selim Cineka59ecc32015-04-07 10:51:49 -07004915 // ANIMATION_TYPE_HEADS_UP_OTHER
4916 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4917
Selim Cinekd9acca52014-09-01 22:33:25 +02004918 // ANIMATION_TYPE_EVERYTHING
4919 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004920 };
4921
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004922 static final int ANIMATION_TYPE_ADD = 0;
4923 static final int ANIMATION_TYPE_REMOVE = 1;
4924 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
4925 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
4926 static final int ANIMATION_TYPE_START_DRAG = 4;
4927 static final int ANIMATION_TYPE_SNAP_BACK = 5;
4928 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
4929 static final int ANIMATION_TYPE_DIMMED = 7;
4930 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04004931 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004932 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02004933 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02004934 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01004935 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004936 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
4937 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004938 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
4939 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
4940 static final int ANIMATION_TYPE_EVERYTHING = 18;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004941
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004942 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
4943 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
4944
Selim Cinek572bbd42014-04-25 16:43:27 +02004945 final long eventStartTime;
4946 final View changingView;
4947 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004948 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004949 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004950 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004951 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07004952 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02004953
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004954 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004955 this(view, type, LENGTHS[type]);
4956 }
4957
Adrian Roos28f90c72017-05-08 17:24:26 -07004958 AnimationEvent(View view, int type, AnimationFilter filter) {
4959 this(view, type, LENGTHS[type], filter);
4960 }
4961
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004962 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07004963 this(view, type, length, FILTERS[type]);
4964 }
4965
4966 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02004967 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
4968 changingView = view;
4969 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004970 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07004971 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004972 }
4973
4974 /**
4975 * Combines the length of several animation events into a single value.
4976 *
4977 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004978 * @return The combined length. Depending on the event types, this might be the maximum of
4979 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004980 */
4981 static long combineLength(ArrayList<AnimationEvent> events) {
4982 long length = 0;
4983 int size = events.size();
4984 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004985 AnimationEvent event = events.get(i);
4986 length = Math.max(length, event.length);
4987 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
4988 return event.length;
4989 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004990 }
4991 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02004992 }
4993 }
Selim Cinek67b22602014-03-10 15:40:16 +01004994}