blob: 291ec1a322aa2f57be674e62361211fc6d0d5623 [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;
Lucas Dupine17ce522017-07-17 15:45:06 -070026import android.annotation.ColorInt;
Selim Cinek31d37b92016-04-26 09:56:42 -070027import android.annotation.FloatRange;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010028import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010029import android.content.Context;
30import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080031import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010032import android.graphics.Canvas;
Selim Cinekd35c2792016-01-21 13:20:57 -080033import android.graphics.Color;
Selim Cinek67b22602014-03-10 15:40:16 +010034import android.graphics.Paint;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010035import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080036import android.graphics.PorterDuff;
37import android.graphics.PorterDuffXfermode;
38import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070039import android.os.Bundle;
Mady Mellor4b80b102016-01-22 08:03:58 -080040import android.os.Handler;
Mady Mellor95d743c2017-01-10 12:05:27 -080041import android.service.notification.StatusBarNotification;
Anthony Chen5e3742e2017-04-07 14:28:44 -070042import android.support.annotation.NonNull;
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;
Jorim Jaggibe565df2014-04-28 17:51:23 +020079import com.android.systemui.statusbar.ExpandableView;
Adrian Roos7d062c42017-03-30 15:11:43 -070080import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070081import com.android.systemui.statusbar.NotificationGuts;
Selim Cinek0cfbef42016-11-09 19:06:36 -080082import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070083import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010084import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020085import com.android.systemui.statusbar.StatusBarState;
Selim Cinek33223572016-02-19 19:32:22 -080086import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080087import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080088import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Selim Cinekb5605e52015-02-20 18:21:41 +010089import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050090import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinekaac93252015-04-14 20:04:12 -070091import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070092import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010093import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +010094
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070095import android.support.v4.graphics.ColorUtils;
96
Selim Cinek707e2072017-06-30 18:32:40 +020097import java.io.FileDescriptor;
98import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +020099import java.util.ArrayList;
Adrian Roos7d062c42017-03-30 15:11:43 -0700100import java.util.Collection;
Selim Cinek33223572016-02-19 19:32:22 -0800101import java.util.Collections;
102import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200103import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800104import java.util.List;
Selim Cinek572bbd42014-04-25 16:43:27 +0200105
Selim Cinek67b22602014-03-10 15:40:16 +0100106/**
107 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
108 */
109public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200110 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800111 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Mady Mellor95d743c2017-01-10 12:05:27 -0800112 NotificationMenuRowPlugin.OnMenuEventListener, ScrollContainer,
Mady Mellor43c2cd12016-12-12 21:05:13 -0800113 VisibilityLocationProvider {
Selim Cinek67b22602014-03-10 15:40:16 +0100114
Selim Cinekd35c2792016-01-21 13:20:57 -0800115 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800116 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100117 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200118 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
119 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200120 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100121 /**
122 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
123 */
124 private static final int INVALID_POINTER = -1;
125
Selim Cinek1408eb52014-06-02 14:45:38 +0200126 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800127 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200128 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100129 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800130 private final Paint mBackgroundPaint = new Paint();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700131 private final boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100132
Selim Cinekbc243a92016-09-27 16:35:13 -0700133 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100134 private int mOwnScrollY;
135 private int mMaxLayoutHeight;
136
137 private VelocityTracker mVelocityTracker;
138 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100139 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100140 private int mTouchSlop;
141 private int mMinimumVelocity;
142 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100143 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200144 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100145 private boolean mIsBeingDragged;
146 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200147 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700148 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100149 private boolean mTouchIsClick;
150 private float mInitialTouchX;
151 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100152
Selim Cinek67b22602014-03-10 15:40:16 +0100153 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100154 private int mContentHeight;
155 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100156 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800157 private int mIncreasedPaddingBetweenElements;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200158 private int mTopPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700159 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700160 private int mBottomInset = 0;
Selim Cinek67b22602014-03-10 15:40:16 +0100161
162 /**
163 * The algorithm which calculates the properties for our children
164 */
Muyuan Li87798022016-04-07 17:51:25 -0700165 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100166
167 /**
168 * The current State this Layout is in
169 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200170 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700171 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100172 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800173 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700174 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
175 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
176 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
177 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
178 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200179 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700180 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
181 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200182 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200183 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200184 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800185 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200186
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200187 /**
188 * The raw amount of the overScroll on the top, which is not rubber-banded.
189 */
190 private float mOverScrolledTopPixels;
191
192 /**
193 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
194 */
195 private float mOverScrolledBottomPixels;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200196 private OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200197 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200198 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100199 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200200 private boolean mNeedsAnimation;
201 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200202 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200203 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400204 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100205 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200206 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200207 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200208 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200209 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200210 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700211 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200212 private boolean mExpandingNotification;
213 private boolean mExpandedInThisMotion;
Muyuan Li84b45612016-06-01 11:05:08 -0700214 protected boolean mScrollingEnabled;
Muyuan Li8cf63dc2016-05-18 17:40:58 -0700215 protected DismissView mDismissView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700216 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400217 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700218 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200219
220 /**
221 * Was the scroller scrolled to the top when the down motion was observed?
222 */
223 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200224 /**
225 * The minimal amount of over scroll which is needed in order to switch to the quick settings
226 * when over scrolling on a expanded card.
227 */
228 private float mMinTopOverScrollToEscape;
229 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700230 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200231 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200232 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700233 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200234 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100235 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700236 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700237
Selim Cinek1408eb52014-06-02 14:45:38 +0200238 /**
239 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
240 * This is needed to avoid scrolling too far after the notification was collapsed in the same
241 * motion.
242 */
243 private int mMaxScrollAfterExpand;
Dan Sandler4247a5c2014-07-23 15:58:08 -0400244 private SwipeHelper.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800245
Mady Mellor95d743c2017-01-10 12:05:27 -0800246 private NotificationMenuRowPlugin mCurrMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800247 private View mTranslatingParentView;
Mady Mellor95d743c2017-01-10 12:05:27 -0800248 private View mMenuExposedView;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700249 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200250
251 /**
252 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
253 * animating.
254 */
255 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700256 private boolean mDisallowDismissInThisMotion;
Jorim Jaggi56306252014-07-03 00:40:09 +0200257 private boolean mInterceptDelegateEnabled;
258 private boolean mDelegateToScrollView;
Selim Cineka59ecc32015-04-07 10:51:49 -0700259 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700260 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200261 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200262 = new ViewTreeObserver.OnPreDrawListener() {
263 @Override
264 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400265 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200266 updateChildren();
267 mChildrenUpdateRequested = false;
268 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200269 return true;
270 }
271 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500272 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100273 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100274 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700275 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek0fccc722015-07-29 17:04:36 -0700276 private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700277 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
278 = new HashSet<>();
279 private HeadsUpManager mHeadsUpManager;
280 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700281 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200282 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700283 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700284 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800285 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700286 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800287 = new ViewTreeObserver.OnPreDrawListener() {
288 @Override
289 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700290 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800291 return true;
292 }
293 };
294 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800295 private Rect mStartAnimationRect = new Rect();
296 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800297 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800298 private boolean mAnimateNextBackgroundBottom;
299 private boolean mAnimateNextBackgroundTop;
300 private ObjectAnimator mBottomAnimator = null;
301 private ObjectAnimator mTopAnimator = null;
302 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
303 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800304 private int mBgColor;
305 private float mDimAmount;
306 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800307 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800308 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
309 @Override
310 public void onAnimationEnd(Animator animation) {
311 mDimAnimator = null;
312 }
313 };
314 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
315 = new ValueAnimator.AnimatorUpdateListener() {
316
317 @Override
318 public void onAnimationUpdate(ValueAnimator animation) {
319 setDimAmount((Float) animation.getAnimatedValue());
320 }
321 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700322 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800323 private boolean mContinuousShadowUpdate;
324 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
325 = new ViewTreeObserver.OnPreDrawListener() {
326
327 @Override
328 public boolean onPreDraw() {
329 updateViewShadows();
330 return true;
331 }
332 };
333 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
334 @Override
335 public int compare(ExpandableView view, ExpandableView otherView) {
336 float endY = view.getTranslationY() + view.getActualHeight();
337 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
338 if (endY < otherEndY) {
339 return -1;
340 } else if (endY > otherEndY) {
341 return 1;
342 } else {
343 // The two notifications end at the same location
344 return 0;
345 }
346 }
347 };
Selim Cinek25503252016-03-03 15:31:43 -0800348 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
Adrian Roos7d062c42017-03-30 15:11:43 -0700349 private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700350 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700351 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800352 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700353 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700354 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400355 private View mForcedScroll;
Selim Cinek972123d2016-05-03 14:25:58 -0700356 private float mBackgroundFadeAmount = 1.0f;
357 private static final Property<NotificationStackScrollLayout, Float> BACKGROUND_FADE =
358 new FloatProperty<NotificationStackScrollLayout>("backgroundFade") {
359 @Override
360 public void setValue(NotificationStackScrollLayout object, float value) {
361 object.setBackgroundFadeAmount(value);
362 }
363
364 @Override
365 public Float get(NotificationStackScrollLayout object) {
366 return object.getBackgroundFadeAmount();
367 }
368 };
Lucas Dupine17ce522017-07-17 15:45:06 -0700369 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700370 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700371 private boolean mForwardScrollable;
372 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700373 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700374 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800375 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700376 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800377 private boolean mNoAmbient;
378 private final Rect mClipRect = new Rect();
379 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700380 private Rect mRequestedClipBounds;
381 private boolean mInHeadsUpPinnedMode;
382 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800383 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100384 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700385 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800386 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100387
388 public NotificationStackScrollLayout(Context context) {
389 this(context, null);
390 }
391
392 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
393 this(context, attrs, 0);
394 }
395
396 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
397 this(context, attrs, defStyleAttr, 0);
398 }
399
400 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
401 int defStyleRes) {
402 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800403 Resources res = getResources();
404
Selim Cinek281c2022016-10-13 19:14:43 -0700405 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800406 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800407 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
408 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200409 mExpandHelper = new ExpandHelper(getContext(), this,
410 minHeight, maxHeight);
411 mExpandHelper.setEventSource(this);
412 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800413 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Selim Cinek1cf41c12014-08-12 20:06:19 +0200414 mSwipeHelper.setLongPressListener(mLongPressListener);
Muyuan Li333a4fc2016-04-16 17:13:46 -0700415 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100416 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800417 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800418 mShouldDrawNotificationBackground =
419 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700420 mFadeNotificationsOnDismiss =
421 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800422
423 updateWillNotDraw();
Selim Cinek67b22602014-03-10 15:40:16 +0100424 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100425 mDebugPaint = new Paint();
426 mDebugPaint.setColor(0xffff0000);
427 mDebugPaint.setStrokeWidth(2);
428 mDebugPaint.setStyle(Paint.Style.STROKE);
429 }
430 }
431
Mady Mellor95d743c2017-01-10 12:05:27 -0800432 public NotificationSwipeActionHelper getSwipeActionHelper() {
433 return mSwipeHelper;
434 }
435
Selim Cinek67b22602014-03-10 15:40:16 +0100436 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800437 public void onMenuClicked(View view, int x, int y, MenuItem item) {
438 if (mLongPressListener == null) {
439 return;
440 }
441 if (view instanceof ExpandableNotificationRow) {
442 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800443 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
444 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800445 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800446 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800447 }
448
449 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800450 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700451 if (mTranslatingParentView != null && row == mTranslatingParentView) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800452 mMenuExposedView = null;
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700453 mTranslatingParentView = null;
454 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000455 }
456
457 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800458 public void onMenuShown(View row) {
459 mMenuExposedView = mTranslatingParentView;
460 if (row instanceof ExpandableNotificationRow) {
461 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
462 ((ExpandableNotificationRow) row).getStatusBarNotification()
463 .getPackageName());
464 }
465 mSwipeHelper.onMenuShown(row);
466 }
467
Selim Cinek67b22602014-03-10 15:40:16 +0100468 protected void onDraw(Canvas canvas) {
Adrian Roosf0b4f962017-05-25 11:53:11 -0700469 if (mShouldDrawNotificationBackground && !mAmbientState.isDark()
470 && mCurrentBounds.top < mCurrentBounds.bottom) {
Selim Cinekd381bc32016-08-15 12:40:57 -0700471 canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom,
472 mBackgroundPaint);
473 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800474
Selim Cinek67b22602014-03-10 15:40:16 +0100475 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800476 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100477 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800478 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100479 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200480 y = getHeight() - getEmptyBottomMargin();
481 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100482 }
483 }
484
Selim Cinekd35c2792016-01-21 13:20:57 -0800485 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800486 // No need to update the background color if it's not being drawn.
487 if (!mShouldDrawNotificationBackground) {
488 return;
489 }
490
Selim Cinekd35c2792016-01-21 13:20:57 -0800491 float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Selim Cinek972123d2016-05-03 14:25:58 -0700492 alpha *= mBackgroundFadeAmount;
Selim Cinekd35c2792016-01-21 13:20:57 -0800493 // We need to manually blend in the background color
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700494 int scrimColor = mScrimController.getBackgroundColor();
495 int color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100496 if (mCachedBackgroundColor != color) {
497 mCachedBackgroundColor = color;
498 mBackgroundPaint.setColor(color);
499 invalidate();
500 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800501 }
502
Selim Cinek67b22602014-03-10 15:40:16 +0100503 private void initView(Context context) {
504 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100505 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200506 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100507 final ViewConfiguration configuration = ViewConfiguration.get(context);
508 mTouchSlop = configuration.getScaledTouchSlop();
509 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
510 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100511 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700512
513 Resources res = context.getResources();
514 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800515 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700516 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700517 mPaddingBetweenElements = Math.max(1,
518 res.getDimensionPixelSize(R.dimen.notification_divider_height));
519 mIncreasedPaddingBetweenElements =
520 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
521 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200522 R.dimen.min_top_overscroll_to_qs);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700523 mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
524 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinek51d21972017-07-19 17:39:20 -0700525 mMinInteractionHeight = res.getDimensionPixelSize(
526 R.dimen.notification_min_interaction_height);
Selim Cineka5eaa602014-05-12 21:27:47 +0200527 }
528
Selim Cinek25503252016-03-03 15:31:43 -0800529 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700530 mDrawBackgroundAsSrc = asSrc;
531 updateSrcDrawing();
532 }
533
534 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800535 if (!mShouldDrawNotificationBackground) {
536 return;
537 }
538
Selim Cinek48ff9b42016-11-09 19:31:51 -0800539 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700540 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800541 invalidate();
542 }
543
Selim Cinekaef92ef2014-06-06 18:06:04 +0200544 private void notifyHeightChangeListener(ExpandableView view) {
545 if (mOnHeightChangedListener != null) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100546 mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200547 }
Selim Cinek67b22602014-03-10 15:40:16 +0100548 }
549
550 @Override
551 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
552 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400553 // We need to measure all children even the GONE ones, such that the heights are calculated
554 // correctly as they are used to calculate how many we can fit on the screen.
555 final int size = getChildCount();
556 for (int i = 0; i < size; i++) {
Anthony Chen04d1ea72017-04-21 14:30:11 -0700557 measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400558 }
Selim Cinek67b22602014-03-10 15:40:16 +0100559 }
560
561 @Override
562 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100563 // we layout all our children centered on the top
564 float centerX = getWidth() / 2.0f;
565 for (int i = 0; i < getChildCount(); i++) {
566 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400567 // We need to layout all children even the GONE ones, such that the heights are
568 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100569 float width = child.getMeasuredWidth();
570 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100571 child.layout((int) (centerX - width / 2.0f),
572 0,
573 (int) (centerX + width / 2.0f),
574 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100575 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200576 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100577 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200578 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200579 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800580 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700581 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100582 }
583
Selim Cinek5bc852a2015-12-21 12:19:09 -0800584 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
585 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200586 mNeedViewResizeAnimation = true;
587 mNeedsAnimation = true;
588 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200589 }
590
Selim Cinekdb167372016-11-17 15:41:17 -0800591 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
592 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800593 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200594 }
595
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200596 public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) {
597 mListener = listener;
598 }
599
Selim Cineka7d4f822016-12-06 14:34:47 -0800600 @Override
601 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
602 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200603 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800604 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200605 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800606 if ((childViewState.location &= ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
607 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100608 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800609 if (row.getVisibility() != View.VISIBLE) {
610 return false;
611 }
612 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200613 }
614
Selim Cinek67b22602014-03-10 15:40:16 +0100615 private void setMaxLayoutHeight(int maxLayoutHeight) {
616 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700617 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200618 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100619 }
620
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200621 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700622 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700623 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700624 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100625 }
626
Selim Cinekbc243a92016-09-27 16:35:13 -0700627 private void updateAlgorithmLayoutMinHeight() {
628 mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
629 }
630
Selim Cinek67b22602014-03-10 15:40:16 +0100631 /**
632 * Updates the children views according to the stack scroll algorithm. Call this whenever
633 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
634 */
635 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800636 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800637 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
638 ? 0
639 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200640 mAmbientState.setScrollY(mOwnScrollY);
641 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200642 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200643 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100644 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200645 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100646 }
647 }
648
Selim Cinekc383fd02016-10-21 15:31:26 -0700649 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700650 mShelf.updateAppearance();
Selim Cinekc383fd02016-10-21 15:31:26 -0700651 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
652 updateBackground();
653 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700654 }
655
Selim Cinek3776fe02016-02-04 13:32:43 -0800656 private void updateScrollStateForAddedChildren() {
657 if (mChildrenToAddAnimated.isEmpty()) {
658 return;
659 }
660 for (int i = 0; i < getChildCount(); i++) {
661 ExpandableView child = (ExpandableView) getChildAt(i);
662 if (mChildrenToAddAnimated.contains(child)) {
663 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800664 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
665 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
666 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800667 int childHeight = getIntrinsicHeight(child) + padding;
668 if (startingPosition < mOwnScrollY) {
669 // This child starts off screen, so let's keep it offscreen to keep the others visible
670
Selim Cinekef406062016-09-29 17:33:13 -0700671 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800672 }
673 }
674 }
675 clampScrollPosition();
676 }
677
Adrian Roos181385c2016-05-05 17:45:44 -0400678 private void updateForcedScroll() {
679 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
680 || !mForcedScroll.isAttachedToWindow())) {
681 mForcedScroll = null;
682 }
683 if (mForcedScroll != null) {
684 ExpandableView expandableView = (ExpandableView) mForcedScroll;
685 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
686 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700687 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400688
689 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700690
691 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
692 // that it is not visible anymore.
693 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700694 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400695 }
696 }
697 }
698
Selim Cinek319bdc42014-05-01 23:01:58 +0200699 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200700 if (!mChildrenUpdateRequested) {
701 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
702 mChildrenUpdateRequested = true;
703 invalidate();
704 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200705 }
706
Selim Cinek67b22602014-03-10 15:40:16 +0100707 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200708 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100709 }
710
Selim Cinekf7a14c02014-07-07 14:01:46 +0200711 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100712 int scrollRange = getScrollRange();
713 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700714 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100715 }
716 }
717
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200718 public int getTopPadding() {
719 return mTopPadding;
720 }
721
Selim Cinek1408eb52014-06-02 14:45:38 +0200722 private void setTopPadding(int topPadding, boolean animate) {
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200723 if (mTopPadding != topPadding) {
724 mTopPadding = topPadding;
725 updateAlgorithmHeightAndPadding();
726 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200727 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200728 mTopPaddingNeedsAnimation = true;
729 mNeedsAnimation = true;
730 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200731 requestChildrenUpdate();
Selim Cinekaef92ef2014-06-06 18:06:04 +0200732 notifyHeightChangeListener(null);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200733 }
734 }
735
736 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700737 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200738 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700739 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200740 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700741 public void setExpandedHeight(float height) {
742 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700743 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800744 int minExpansionHeight = getMinExpansionHeight();
745 if (height < minExpansionHeight) {
746 mClipRect.left = 0;
747 mClipRect.right = getWidth();
748 mClipRect.top = 0;
749 mClipRect.bottom = (int) height;
750 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700751 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800752 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700753 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800754 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200755 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700756 float translationY;
757 float appearEndPosition = getAppearEndPosition();
758 float appearStartPosition = getAppearStartPosition();
759 if (height >= appearEndPosition) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700760 translationY = 0;
Selim Cinek94c2d822016-07-13 18:50:04 -0700761 stackHeight = (int) height;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200762 } else {
Selim Cinek94c2d822016-07-13 18:50:04 -0700763 float appearFraction = getAppearFraction(height);
764 if (appearFraction >= 0) {
765 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
766 appearFraction);
767 } else {
768 // This may happen when pushing up a heads up. We linearly push it up from the
769 // start
770 translationY = height - appearStartPosition + getExpandTranslationStart();
771 }
772 stackHeight = (int) (height - translationY);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200773 }
774 if (stackHeight != mCurrentStackHeight) {
775 mCurrentStackHeight = stackHeight;
776 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200777 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200778 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700779 setStackTranslation(translationY);
Selim Cinekcafa87f2016-10-26 17:00:17 -0700780 }
781
782 private void setRequestedClipBounds(Rect clipRect) {
783 mRequestedClipBounds = clipRect;
784 updateClipping();
785 }
786
787 public void updateClipping() {
788 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
789 && !mHeadsUpAnimatingAway;
790 if (mIsClipped != clipped) {
791 mIsClipped = clipped;
792 updateFadingState();
793 }
794 if (clipped) {
795 setClipBounds(mRequestedClipBounds);
796 } else {
797 setClipBounds(null);
798 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700799 }
800
801 /**
802 * @return The translation at the beginning when expanding.
803 * Measured relative to the resting position.
804 */
805 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800806 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700807 }
808
809 /**
810 * @return the position from where the appear transition starts when expanding.
811 * Measured in absolute height.
812 */
813 private float getAppearStartPosition() {
Selim Cinekd127d792016-11-01 19:11:41 -0700814 if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
Selim Cinekebf42342017-07-13 15:46:10 +0200815 if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) {
Selim Cinekd127d792016-11-01 19:11:41 -0700816 // If we ever expanded beyond the first notification, it's allowed to merge into
817 // the shelf
818 return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
819 }
820 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800821 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700822 }
823
824 /**
825 * @return the position from where the appear transition ends when expanding.
826 * Measured in absolute height.
827 */
828 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -0700829 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -0700830 int notGoneChildCount = getNotGoneChildCount();
831 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100832 int minNotificationsForShelf = 1;
Selim Cinekebf42342017-07-13 15:46:10 +0200833 if (mTrackingHeadsUp
834 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100835 appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
836 minNotificationsForShelf = 2;
837 } else {
838 appearPosition = 0;
839 }
Selim Cinek66440cf2017-05-26 13:48:47 -0700840 if (notGoneChildCount >= minNotificationsForShelf
Anthony Chen9e05d462017-04-07 10:10:21 -0700841 && mShelf.getVisibility() != GONE) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100842 appearPosition += mShelf.getIntrinsicHeight();
843 }
Selim Cinekaa417da2016-10-27 18:17:08 -0700844 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +0100845 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -0700846 }
847 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -0700848 }
849
850 /**
851 * @param height the height of the panel
852 * @return the fraction of the appear animation that has been performed
853 */
854 public float getAppearFraction(float height) {
855 float appearEndPosition = getAppearEndPosition();
856 float appearStartPosition = getAppearStartPosition();
857 return (height - appearStartPosition)
858 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700859 }
860
Selim Cinekd2281152015-04-10 14:37:46 -0700861 public float getStackTranslation() {
862 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700863 }
864
Selim Cinekd2281152015-04-10 14:37:46 -0700865 private void setStackTranslation(float stackTranslation) {
866 if (stackTranslation != mStackTranslation) {
867 mStackTranslation = stackTranslation;
868 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700869 requestChildrenUpdate();
870 }
Selim Cinek67b22602014-03-10 15:40:16 +0100871 }
872
873 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100874 * 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 +0100875 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
876 *
877 * @return either the layout height or the externally defined height, whichever is smaller
878 */
Selim Cinek343e6e22014-04-11 21:23:30 +0200879 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +0100880 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
881 }
882
Selim Cinek816c8e42015-11-19 12:00:45 -0800883 public int getFirstItemMinHeight() {
884 final ExpandableView firstChild = getFirstChildNotGone();
885 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100886 }
887
Dan Sandler4247a5c2014-07-23 15:58:08 -0400888 public void setLongPressListener(SwipeHelper.LongPressListener listener) {
Selim Cinek67b22602014-03-10 15:40:16 +0100889 mSwipeHelper.setLongPressListener(listener);
Dan Sandler4247a5c2014-07-23 15:58:08 -0400890 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +0100891 }
892
Jason Monk16ac3772016-02-10 15:39:21 -0500893 public void setQsContainer(ViewGroup qsContainer) {
894 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +0200895 }
896
Mady Mellorb0a82462016-04-30 17:31:02 -0700897 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100898 public void onChildDismissed(View v) {
Selim Cinekd1395642016-04-28 12:22:42 -0700899 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
900 if (!row.isDismissed()) {
901 handleChildDismissed(v);
902 }
903 ViewGroup transientContainer = row.getTransientContainer();
904 if (transientContainer != null) {
905 transientContainer.removeTransientView(v);
906 }
907 }
908
909 private void handleChildDismissed(View v) {
Dan Sandlereceda3d2014-07-21 15:35:01 -0400910 if (mDismissAllInProgress) {
911 return;
912 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100913 setSwipingInProgress(false);
Selim Cinekeb973562014-05-02 17:07:49 +0200914 if (mDragAnimPendingChildren.contains(v)) {
915 // We start the swipe and finish it in the same frame, we don't want any animation
916 // for the drag
917 mDragAnimPendingChildren.remove(v);
918 }
Selim Cinek572bbd42014-04-25 16:43:27 +0200919 mSwipedOutViews.add(v);
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200920 mAmbientState.onDragFinished(v);
Selim Cinek33223572016-02-19 19:32:22 -0800921 updateContinuousShadowDrawing();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700922 if (v instanceof ExpandableNotificationRow) {
923 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
924 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -0700925 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700926 }
927 }
Selim Cineke9bad242016-06-15 11:46:37 -0700928 performDismiss(v, mGroupManager, false /* fromAccessibility */);
Blazej Magnowski72323322015-07-24 11:49:40 -0700929
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700930 mFalsingManager.onNotificationDismissed();
931 if (mFalsingManager.shouldEnforceBouncer()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500932 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
Felipe Lemeee5d6302016-04-22 16:11:19 -0700933 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -0700934 }
Selim Cinekeb973562014-05-02 17:07:49 +0200935 }
936
Selim Cineke9bad242016-06-15 11:46:37 -0700937 public static void performDismiss(View v, NotificationGroupManager groupManager,
938 boolean fromAccessibility) {
Selim Cinek9e624e72016-07-20 13:46:49 -0700939 if (!(v instanceof ExpandableNotificationRow)) {
940 return;
Selim Cinek2a739342016-03-17 10:28:55 -0700941 }
Selim Cinek9e624e72016-07-20 13:46:49 -0700942 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
943 if (groupManager.isOnlyChildInGroup(row.getStatusBarNotification())) {
944 ExpandableNotificationRow groupSummary =
945 groupManager.getLogicalGroupSummary(row.getStatusBarNotification());
946 if (groupSummary.isClearable()) {
947 performDismiss(groupSummary, groupManager, fromAccessibility);
948 }
949 }
950 row.setDismissed(true, fromAccessibility);
951 if (row.isClearable()) {
952 row.performDismiss();
Selim Cinek2a739342016-03-17 10:28:55 -0700953 }
954 if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
955 }
956
Selim Cinekeb973562014-05-02 17:07:49 +0200957 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -0800958 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200959 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -0800960 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +0200961 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +0200962 if (mAnimationsEnabled) {
963 mSnappedBackChildren.add(animView);
964 mNeedsAnimation = true;
965 }
Selim Cinekeb973562014-05-02 17:07:49 +0200966 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +0200967 } else {
968 // We start the swipe and snap back in the same frame, we don't want any animation
969 mDragAnimPendingChildren.remove(animView);
970 }
Mady Mellor95d743c2017-01-10 12:05:27 -0800971 if (mCurrMenuRow != null && targetLeft == 0) {
972 mCurrMenuRow.resetMenu();
973 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -0800974 }
Selim Cinek67b22602014-03-10 15:40:16 +0100975 }
976
Adrian Roos5d9cc662014-05-28 17:08:13 +0200977 @Override
978 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Selim Cinek131c1e22015-05-11 19:04:49 -0700979 if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
Selim Cinekaac93252015-04-14 20:04:12 -0700980 mScrimController.setTopHeadsUpDragAmount(animView,
Winsonbde852d2016-04-15 19:06:54 -0700981 Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
Selim Cinekaac93252015-04-14 20:04:12 -0700982 }
Anthony Chen7acbb772017-04-07 16:45:25 -0700983 // Returning true prevents alpha fading.
984 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +0200985 }
986
Mady Mellorb0a82462016-04-30 17:31:02 -0700987 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100988 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700989 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100990 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200991 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -0800992 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -0700993 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +0200994 mDragAnimPendingChildren.add(v);
995 mNeedsAnimation = true;
996 }
Selim Cinekeb973562014-05-02 17:07:49 +0200997 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +0100998 }
999
Selim Cinek684a4422015-04-15 16:18:39 -07001000 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001001 if (v instanceof ExpandableNotificationRow) {
1002 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001003 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001004 }
1005 return false;
1006 }
1007
1008 private boolean isHeadsUp(View v) {
1009 if (v instanceof ExpandableNotificationRow) {
1010 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1011 return row.isHeadsUp();
1012 }
1013 return false;
1014 }
1015
Mady Mellorb0a82462016-04-30 17:31:02 -07001016 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001017 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001018 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001019 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001020 }
1021
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001022 @Override
1023 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001024 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001025 }
1026
Mady Mellor4b80b102016-01-22 08:03:58 -08001027 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001028 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001029 View child = getChildAtPosition(ev.getX(), ev.getY());
1030 if (child instanceof ExpandableNotificationRow) {
1031 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1032 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001033 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001034 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001035 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001036 || (parent.getNotificationChildren().size() == 1
1037 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001038 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001039 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001040 // child notifications so we use the parent of the child. We also do the same
1041 // if we only have a single child.
1042 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001043 }
1044 }
1045 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001046 }
1047
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001048 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1049 getLocationOnScreen(mTempInt2);
1050 float localTouchY = touchY - mTempInt2[1];
1051
1052 ExpandableView closestChild = null;
1053 float minDist = Float.MAX_VALUE;
1054
1055 // find the view closest to the location, accounting for GONE views
1056 final int count = getChildCount();
1057 for (int childIdx = 0; childIdx < count; childIdx++) {
1058 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1059 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001060 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001061 continue;
1062 }
1063 float childTop = slidingChild.getTranslationY();
1064 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001065 float bottom = childTop + slidingChild.getActualHeight()
1066 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001067
1068 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1069 if (dist < minDist) {
1070 closestChild = slidingChild;
1071 minDist = dist;
1072 }
1073 }
1074 return closestChild;
1075 }
1076
Mady Mellorb0a82462016-04-30 17:31:02 -07001077 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001078 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001079 getLocationOnScreen(mTempInt2);
1080 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001081 }
1082
Mady Mellorb0a82462016-04-30 17:31:02 -07001083 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001084 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001085 // find the view under the pointer, accounting for GONE views
1086 final int count = getChildCount();
1087 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001088 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001089 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001090 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001091 continue;
1092 }
Selim Cinek89faff12014-06-19 16:29:04 -07001093 float childTop = slidingChild.getTranslationY();
1094 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001095 float bottom = childTop + slidingChild.getActualHeight()
1096 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001097
1098 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1099 // camera affordance).
1100 int left = 0;
1101 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001102
Selim Cinek51d21972017-07-19 17:39:20 -07001103 if (bottom - top >= mMinInteractionHeight
1104 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001105 if (slidingChild instanceof ExpandableNotificationRow) {
1106 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001107 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Selim Cinek5bc852a2015-12-21 12:19:09 -08001108 && mHeadsUpManager.getTopEntry().entry.row != row
1109 && mGroupManager.getGroupSummary(
1110 mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
1111 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001112 continue;
1113 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001114 return row.getViewAtPosition(touchY - childTop);
1115 }
Selim Cinek67b22602014-03-10 15:40:16 +01001116 return slidingChild;
1117 }
1118 }
1119 return null;
1120 }
1121
Mady Mellorb0a82462016-04-30 17:31:02 -07001122 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001123 public boolean canChildBeExpanded(View v) {
1124 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001125 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001126 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001127 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001128 }
1129
Chris Wren6abeeb92016-05-26 14:44:38 -04001130 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001131 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001132 public void setUserExpandedChild(View v, boolean userExpanded) {
1133 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001134 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001135 if (userExpanded && onKeyguard()) {
1136 // Due to a race when locking the screen while touching, a notification may be
1137 // expanded even after we went back to keyguard. An example of this happens if
1138 // you click in the empty space while expanding a group.
1139
1140 // We also need to un-user lock it here, since otherwise the content height
1141 // calculated might be wrong. We also can't invert the two calls since
1142 // un-userlocking it will trigger a layout switch in the content view.
1143 row.setUserLocked(false);
1144 updateContentHeight();
1145 notifyHeightChangeListener(row);
1146 return;
1147 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001148 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1149 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001150 }
1151 }
1152
Mady Mellorb0a82462016-04-30 17:31:02 -07001153 @Override
1154 public void setExpansionCancelled(View v) {
1155 if (v instanceof ExpandableNotificationRow) {
1156 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1157 }
1158 }
1159
1160 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001161 public void setUserLockedChild(View v, boolean userLocked) {
1162 if (v instanceof ExpandableNotificationRow) {
1163 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1164 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001165 removeLongPressCallback();
1166 requestDisallowInterceptTouchEvent(true);
1167 }
1168
1169 @Override
1170 public void expansionStateChanged(boolean isExpanding) {
1171 mExpandingNotification = isExpanding;
1172 if (!mExpandedInThisMotion) {
1173 mMaxScrollAfterExpand = mOwnScrollY;
1174 mExpandedInThisMotion = true;
1175 }
1176 }
1177
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001178 @Override
1179 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001180 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001181 }
1182
Selim Cinek1408eb52014-06-02 14:45:38 +02001183 public void setScrollingEnabled(boolean enable) {
1184 mScrollingEnabled = enable;
1185 }
1186
Adrian Roos181385c2016-05-05 17:45:44 -04001187 @Override
1188 public void lockScrollTo(View v) {
1189 if (mForcedScroll == v) {
1190 return;
1191 }
1192 mForcedScroll = v;
1193 scrollTo(v);
1194 }
1195
1196 @Override
Ricky Waicd35def2016-05-03 11:07:07 +01001197 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001198 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001199 int positionInLinearLayout = getPositionInLinearLayout(v);
1200 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1201 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001202
Adrian Roos4a579672016-05-24 16:54:37 -07001203 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1204 // that it is not visible anymore.
1205 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001206 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1207 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001208 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001209 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001210 }
Ricky Waicd35def2016-05-03 11:07:07 +01001211 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001212 }
1213
Adrian Roos181385c2016-05-05 17:45:44 -04001214 /**
1215 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1216 * the IME.
1217 */
1218 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1219 return positionInLinearLayout + v.getIntrinsicHeight() +
1220 getImeInset() - getHeight() + getTopPadding();
1221 }
1222
Adrian Roos5153d4a2016-03-22 10:01:56 -07001223 @Override
1224 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001225 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001226
1227 int range = getScrollRange();
1228 if (mOwnScrollY > range) {
1229 // HACK: We're repeatedly getting staggered insets here while the IME is
1230 // animating away. To work around that we'll wait until things have settled.
1231 removeCallbacks(mReclamp);
1232 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001233 } else if (mForcedScroll != null) {
1234 // The scroll was requested before we got the actual inset - in case we need
1235 // to scroll up some more do so now.
1236 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001237 }
1238 return insets;
1239 }
1240
1241 private Runnable mReclamp = new Runnable() {
1242 @Override
1243 public void run() {
1244 int range = getScrollRange();
1245 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1246 mDontReportNextOverScroll = true;
1247 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001248 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001249 }
1250 };
1251
Selim Cinek1408eb52014-06-02 14:45:38 +02001252 public void setExpandingEnabled(boolean enable) {
1253 mExpandHelper.setEnabled(enable);
1254 }
1255
1256 private boolean isScrollingEnabled() {
1257 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001258 }
1259
Mady Mellorb0a82462016-04-30 17:31:02 -07001260 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001261 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001262 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001263 }
1264
Selim Cinek19c8c702014-08-25 22:09:19 +02001265 @Override
1266 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001267 return onKeyguard();
1268 }
1269
1270 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001271 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001272 }
1273
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001274 private void setSwipingInProgress(boolean isSwiped) {
1275 mSwipingInProgress = isSwiped;
1276 if(isSwiped) {
1277 requestDisallowInterceptTouchEvent(true);
1278 }
Selim Cinek67b22602014-03-10 15:40:16 +01001279 }
1280
1281 @Override
1282 protected void onConfigurationChanged(Configuration newConfig) {
1283 super.onConfigurationChanged(newConfig);
1284 float densityScale = getResources().getDisplayMetrics().density;
1285 mSwipeHelper.setDensityScale(densityScale);
1286 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1287 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1288 initView(getContext());
1289 }
1290
Dan Sandlereceda3d2014-07-21 15:35:01 -04001291 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001292 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1293 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001294 }
1295
Mady Mellor86889c22016-04-18 16:37:06 -07001296 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001297 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001298 // If the child is showing the notification menu snap to that
1299 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001300 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001301 }
1302
Selim Cinek67b22602014-03-10 15:40:16 +01001303 @Override
1304 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001305 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1306 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001307 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001308 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001309 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001310 if (isCancelOrUp) {
1311 mExpandHelper.onlyObserveMovements(false);
1312 }
1313 boolean wasExpandingBefore = mExpandingNotification;
1314 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001315 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1316 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001317 dispatchDownEventToScroller(ev);
1318 }
1319 }
Selim Cinek67b22602014-03-10 15:40:16 +01001320 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001321 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1322 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001323 scrollerWantsIt = onScrollTouch(ev);
1324 }
1325 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001326 if (!mIsBeingDragged
1327 && !mExpandingNotification
1328 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001329 && !mOnlyScrollingInThisMotion
1330 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001331 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1332 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001333
1334 // Check if we need to clear any snooze leavebehinds
1335 NotificationGuts guts = mStatusBar.getExposedGuts();
1336 if (guts != null && !isTouchInView(ev, guts)
1337 && guts.getGutsContent() instanceof NotificationSnooze) {
1338 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1339 if ((ns.isExpanded() && isCancelOrUp)
1340 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1341 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1342 // clear it on the next non-horizontal swipe or expand event.
1343 checkSnoozeLeavebehind();
1344 }
1345 }
1346 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1347 mCheckForLeavebehind = true;
1348 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001349 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1350 }
1351
1352 private void dispatchDownEventToScroller(MotionEvent ev) {
1353 MotionEvent downEvent = MotionEvent.obtain(ev);
1354 downEvent.setAction(MotionEvent.ACTION_DOWN);
1355 onScrollTouch(downEvent);
1356 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001357 }
1358
Selim Cinek11e33232016-08-05 15:30:53 -07001359 @Override
1360 public boolean onGenericMotionEvent(MotionEvent event) {
1361 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1362 || mDisallowScrollingInThisMotion) {
1363 return false;
1364 }
1365 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1366 switch (event.getAction()) {
1367 case MotionEvent.ACTION_SCROLL: {
1368 if (!mIsBeingDragged) {
1369 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1370 if (vscroll != 0) {
1371 final int delta = (int) (vscroll * getVerticalScrollFactor());
1372 final int range = getScrollRange();
1373 int oldScrollY = mOwnScrollY;
1374 int newScrollY = oldScrollY - delta;
1375 if (newScrollY < 0) {
1376 newScrollY = 0;
1377 } else if (newScrollY > range) {
1378 newScrollY = range;
1379 }
1380 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001381 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001382 return true;
1383 }
1384 }
1385 }
1386 }
1387 }
1388 }
1389 return super.onGenericMotionEvent(event);
1390 }
1391
Selim Cinek67b22602014-03-10 15:40:16 +01001392 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001393 if (!isScrollingEnabled()) {
1394 return false;
1395 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001396 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001397 return false;
1398 }
Adrian Roos181385c2016-05-05 17:45:44 -04001399 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001400 initVelocityTrackerIfNotExists();
1401 mVelocityTracker.addMovement(ev);
1402
1403 final int action = ev.getAction();
1404
1405 switch (action & MotionEvent.ACTION_MASK) {
1406 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001407 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001408 return false;
1409 }
1410 boolean isBeingDragged = !mScroller.isFinished();
1411 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001412 /*
1413 * If being flinged and user touches, stop the fling. isFinished
1414 * will be false if being flinged.
1415 */
1416 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001417 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001418 }
1419
1420 // Remember where the motion event started
1421 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001422 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001423 mActivePointerId = ev.getPointerId(0);
1424 break;
1425 }
1426 case MotionEvent.ACTION_MOVE:
1427 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1428 if (activePointerIndex == -1) {
1429 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1430 break;
1431 }
1432
1433 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001434 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001435 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001436 final int xDiff = Math.abs(x - mDownX);
1437 final int yDiff = Math.abs(deltaY);
1438 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001439 setIsBeingDragged(true);
1440 if (deltaY > 0) {
1441 deltaY -= mTouchSlop;
1442 } else {
1443 deltaY += mTouchSlop;
1444 }
1445 }
1446 if (mIsBeingDragged) {
1447 // Scroll to follow the motion event
1448 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001449 int range = getScrollRange();
1450 if (mExpandedInThisMotion) {
1451 range = Math.min(range, mMaxScrollAfterExpand);
1452 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001453
1454 float scrollAmount;
1455 if (deltaY < 0) {
1456 scrollAmount = overScrollDown(deltaY);
1457 } else {
1458 scrollAmount = overScrollUp(deltaY, range);
1459 }
Selim Cinek67b22602014-03-10 15:40:16 +01001460
Selim Cinek9212de82017-02-06 16:04:28 -08001461 // Calling customOverScrollBy will call onCustomOverScrolled, which
1462 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001463 if (scrollAmount != 0.0f) {
1464 // The scrolling motion could not be compensated with the
1465 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001466 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1467 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001468 // If we're scrolling, leavebehinds should be dismissed
1469 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001470 }
Selim Cinek67b22602014-03-10 15:40:16 +01001471 }
1472 break;
1473 case MotionEvent.ACTION_UP:
1474 if (mIsBeingDragged) {
1475 final VelocityTracker velocityTracker = mVelocityTracker;
1476 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1477 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1478
Selim Cinek1408eb52014-06-02 14:45:38 +02001479 if (shouldOverScrollFling(initialVelocity)) {
1480 onOverScrollFling(true, initialVelocity);
1481 } else {
1482 if (getChildCount() > 0) {
1483 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1484 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1485 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1486 fling(-initialVelocity);
1487 } else {
1488 onOverScrollFling(false, initialVelocity);
1489 }
1490 } else {
1491 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1492 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001493 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001494 }
Selim Cinek67b22602014-03-10 15:40:16 +01001495 }
1496 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001497 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001498 mActivePointerId = INVALID_POINTER;
1499 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001500 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001501
Selim Cinek67b22602014-03-10 15:40:16 +01001502 break;
1503 case MotionEvent.ACTION_CANCEL:
1504 if (mIsBeingDragged && getChildCount() > 0) {
1505 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001506 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001507 }
1508 mActivePointerId = INVALID_POINTER;
1509 endDrag();
1510 }
1511 break;
1512 case MotionEvent.ACTION_POINTER_DOWN: {
1513 final int index = ev.getActionIndex();
1514 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001515 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001516 mActivePointerId = ev.getPointerId(index);
1517 break;
1518 }
1519 case MotionEvent.ACTION_POINTER_UP:
1520 onSecondaryPointerUp(ev);
1521 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001522 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001523 break;
1524 }
1525 return true;
1526 }
1527
Muyuan Li84b45612016-06-01 11:05:08 -07001528 protected boolean isInsideQsContainer(MotionEvent ev) {
1529 return ev.getY() < mQsContainer.getBottom();
1530 }
1531
Selim Cinek1408eb52014-06-02 14:45:38 +02001532 private void onOverScrollFling(boolean open, int initialVelocity) {
1533 if (mOverscrollTopChangedListener != null) {
1534 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1535 }
1536 mDontReportNextOverScroll = true;
1537 setOverScrollAmount(0.0f, true, false);
1538 }
1539
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001540 /**
1541 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1542 *
1543 * @param deltaY The amount to scroll upwards, has to be positive.
1544 * @return The amount of scrolling to be performed by the scroller,
1545 * not handled by the overScroll amount.
1546 */
1547 private float overScrollUp(int deltaY, int range) {
1548 deltaY = Math.max(deltaY, 0);
1549 float currentTopAmount = getCurrentOverScrollAmount(true);
1550 float newTopAmount = currentTopAmount - deltaY;
1551 if (currentTopAmount > 0) {
1552 setOverScrollAmount(newTopAmount, true /* onTop */,
1553 false /* animate */);
1554 }
1555 // Top overScroll might not grab all scrolling motion,
1556 // we have to scroll as well.
1557 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1558 float newScrollY = mOwnScrollY + scrollAmount;
1559 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001560 if (!mExpandedInThisMotion) {
1561 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1562 // We overScroll on the top
1563 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1564 false /* onTop */,
1565 false /* animate */);
1566 }
Selim Cinekef406062016-09-29 17:33:13 -07001567 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001568 scrollAmount = 0.0f;
1569 }
1570 return scrollAmount;
1571 }
1572
1573 /**
1574 * Perform a scroll downward and adapt the overscroll amounts accordingly
1575 *
1576 * @param deltaY The amount to scroll downwards, has to be negative.
1577 * @return The amount of scrolling to be performed by the scroller,
1578 * not handled by the overScroll amount.
1579 */
1580 private float overScrollDown(int deltaY) {
1581 deltaY = Math.min(deltaY, 0);
1582 float currentBottomAmount = getCurrentOverScrollAmount(false);
1583 float newBottomAmount = currentBottomAmount + deltaY;
1584 if (currentBottomAmount > 0) {
1585 setOverScrollAmount(newBottomAmount, false /* onTop */,
1586 false /* animate */);
1587 }
1588 // Bottom overScroll might not grab all scrolling motion,
1589 // we have to scroll as well.
1590 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1591 float newScrollY = mOwnScrollY + scrollAmount;
1592 if (newScrollY < 0) {
1593 float currentTopPixels = getCurrentOverScrolledPixels(true);
1594 // We overScroll on the top
1595 setOverScrolledPixels(currentTopPixels - newScrollY,
1596 true /* onTop */,
1597 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001598 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001599 scrollAmount = 0.0f;
1600 }
1601 return scrollAmount;
1602 }
1603
Selim Cinek67b22602014-03-10 15:40:16 +01001604 private void onSecondaryPointerUp(MotionEvent ev) {
1605 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1606 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1607 final int pointerId = ev.getPointerId(pointerIndex);
1608 if (pointerId == mActivePointerId) {
1609 // This was our active pointer going up. Choose a new
1610 // active pointer and adjust accordingly.
1611 // TODO: Make this decision more intelligent.
1612 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1613 mLastMotionY = (int) ev.getY(newPointerIndex);
1614 mActivePointerId = ev.getPointerId(newPointerIndex);
1615 if (mVelocityTracker != null) {
1616 mVelocityTracker.clear();
1617 }
1618 }
1619 }
1620
1621 private void initVelocityTrackerIfNotExists() {
1622 if (mVelocityTracker == null) {
1623 mVelocityTracker = VelocityTracker.obtain();
1624 }
1625 }
1626
1627 private void recycleVelocityTracker() {
1628 if (mVelocityTracker != null) {
1629 mVelocityTracker.recycle();
1630 mVelocityTracker = null;
1631 }
1632 }
1633
1634 private void initOrResetVelocityTracker() {
1635 if (mVelocityTracker == null) {
1636 mVelocityTracker = VelocityTracker.obtain();
1637 } else {
1638 mVelocityTracker.clear();
1639 }
1640 }
1641
Ricky Waicd35def2016-05-03 11:07:07 +01001642 public void setFinishScrollingCallback(Runnable runnable) {
1643 mFinishScrollingCallback = runnable;
1644 }
1645
Selim Cinek9212de82017-02-06 16:04:28 -08001646 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001647 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001648 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001649 int y = mScroller.getCurrY();
1650
Selim Cinek9212de82017-02-06 16:04:28 -08001651 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001652 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001653 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1654 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001655 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001656 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1657 }
1658 }
Selim Cinek67b22602014-03-10 15:40:16 +01001659
Adrian Roos5153d4a2016-03-22 10:01:56 -07001660 if (mDontClampNextScroll) {
1661 range = Math.max(range, oldY);
1662 }
Selim Cinek9212de82017-02-06 16:04:28 -08001663 customOverScrollBy(y - oldY, oldY, range,
1664 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001665 }
1666
Selim Cinek9212de82017-02-06 16:04:28 -08001667 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001668 } else {
1669 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001670 if (mFinishScrollingCallback != null) {
1671 mFinishScrollingCallback.run();
1672 }
Selim Cinek67b22602014-03-10 15:40:16 +01001673 }
1674 }
1675
Selim Cinek9212de82017-02-06 16:04:28 -08001676 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1677 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001678
1679 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001680 final int top = -maxOverScrollY;
1681 final int bottom = maxOverScrollY + scrollRangeY;
1682
1683 boolean clampedY = false;
1684 if (newScrollY > bottom) {
1685 newScrollY = bottom;
1686 clampedY = true;
1687 } else if (newScrollY < top) {
1688 newScrollY = top;
1689 clampedY = true;
1690 }
1691
Selim Cinek9212de82017-02-06 16:04:28 -08001692 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001693
1694 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001695 }
1696
1697 /**
1698 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1699 * overscroll effect based on numPixels. By default this will also cancel animations on the
1700 * same overScroll edge.
1701 *
1702 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1703 * the rubber-banding logic.
1704 * @param onTop Should the effect be applied on top of the scroller.
1705 * @param animate Should an animation be performed.
1706 */
1707 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001708 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001709 }
1710
1711 /**
1712 * Set the effective overScroll amount which will be directly reflected in the layout.
1713 * By default this will also cancel animations on the same overScroll edge.
1714 *
1715 * @param amount The amount to overScroll by.
1716 * @param onTop Should the effect be applied on top of the scroller.
1717 * @param animate Should an animation be performed.
1718 */
1719 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1720 setOverScrollAmount(amount, onTop, animate, true);
1721 }
1722
1723 /**
1724 * Set the effective overScroll amount which will be directly reflected in the layout.
1725 *
1726 * @param amount The amount to overScroll by.
1727 * @param onTop Should the effect be applied on top of the scroller.
1728 * @param animate Should an animation be performed.
1729 * @param cancelAnimators Should running animations be cancelled.
1730 */
1731 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1732 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001733 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1734 }
1735
1736 /**
1737 * Set the effective overScroll amount which will be directly reflected in the layout.
1738 *
1739 * @param amount The amount to overScroll by.
1740 * @param onTop Should the effect be applied on top of the scroller.
1741 * @param animate Should an animation be performed.
1742 * @param cancelAnimators Should running animations be cancelled.
1743 * @param isRubberbanded The value which will be passed to
1744 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1745 */
1746 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1747 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001748 if (cancelAnimators) {
1749 mStateAnimator.cancelOverScrollAnimators(onTop);
1750 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001751 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001752 }
1753
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001754 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1755 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001756 amount = Math.max(0, amount);
1757 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001758 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001759 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001760 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001761 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001762 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001763 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001764 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001765 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001766 }
1767 }
1768
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001769 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001770 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1771 if (mDontReportNextOverScroll) {
1772 mDontReportNextOverScroll = false;
1773 return;
1774 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001775 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001776 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001777 }
1778 }
1779
1780 public void setOverscrollTopChangedListener(
1781 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1782 mOverscrollTopChangedListener = overscrollTopChangedListener;
1783 }
1784
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001785 public float getCurrentOverScrollAmount(boolean top) {
1786 return mAmbientState.getOverScrollAmount(top);
1787 }
1788
1789 public float getCurrentOverScrolledPixels(boolean top) {
1790 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
1791 }
1792
1793 private void setOverScrolledPixels(float amount, boolean onTop) {
1794 if (onTop) {
1795 mOverScrolledTopPixels = amount;
1796 } else {
1797 mOverScrolledBottomPixels = amount;
1798 }
1799 }
1800
Selim Cinek9212de82017-02-06 16:04:28 -08001801 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001802 // Treat animating scrolls differently; see #computeScroll() for why.
1803 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07001804 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001805 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001806 springBack();
1807 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02001808 float overScrollTop = getCurrentOverScrollAmount(true);
1809 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001810 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001811 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001812 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001813 }
Selim Cinek67b22602014-03-10 15:40:16 +01001814 }
Selim Cinek67b22602014-03-10 15:40:16 +01001815 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08001816 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001817 }
1818 }
1819
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001820 private void springBack() {
1821 int scrollRange = getScrollRange();
1822 boolean overScrolledTop = mOwnScrollY <= 0;
1823 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
1824 if (overScrolledTop || overScrolledBottom) {
1825 boolean onTop;
1826 float newAmount;
1827 if (overScrolledTop) {
1828 onTop = true;
1829 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07001830 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02001831 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001832 } else {
1833 onTop = false;
1834 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07001835 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001836 }
1837 setOverScrollAmount(newAmount, onTop, false);
1838 setOverScrollAmount(0.0f, onTop, true);
1839 mScroller.forceFinished(true);
1840 }
1841 }
1842
Selim Cinek67b22602014-03-10 15:40:16 +01001843 private int getScrollRange() {
Selim Cineka424c502016-04-05 13:07:54 -07001844 int contentHeight = getContentHeight();
Selim Cinekdb167372016-11-17 15:41:17 -08001845 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07001846 int imeInset = getImeInset();
1847 scrollRange += Math.min(imeInset, Math.max(0,
1848 getContentHeight() - (getHeight() - imeInset)));
1849 return scrollRange;
1850 }
1851
1852 private int getImeInset() {
1853 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01001854 }
1855
Selim Cinek343e6e22014-04-11 21:23:30 +02001856 /**
1857 * @return the first child which has visibility unequal to GONE
1858 */
Selim Cinekb55386d2015-12-16 17:26:49 -08001859 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02001860 int childCount = getChildCount();
1861 for (int i = 0; i < childCount; i++) {
1862 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001863 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08001864 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02001865 }
1866 }
1867 return null;
1868 }
1869
Selim Cinek4a1ac842014-05-01 15:51:58 +02001870 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001871 * @return the child before the given view which has visibility unequal to GONE
1872 */
1873 public ExpandableView getViewBeforeView(ExpandableView view) {
1874 ExpandableView previousView = null;
1875 int childCount = getChildCount();
1876 for (int i = 0; i < childCount; i++) {
1877 View child = getChildAt(i);
1878 if (child == view) {
1879 return previousView;
1880 }
1881 if (child.getVisibility() != View.GONE) {
1882 previousView = (ExpandableView) child;
1883 }
1884 }
1885 return null;
1886 }
1887
1888 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001889 * @return The first child which has visibility unequal to GONE which is currently below the
1890 * given translationY or equal to it.
1891 */
Selim Cinekef8c2252017-02-10 14:52:18 -08001892 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001893 int childCount = getChildCount();
1894 for (int i = 0; i < childCount; i++) {
1895 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08001896 if (child.getVisibility() == View.GONE) {
1897 continue;
1898 }
1899 float rowTranslation = child.getTranslationY();
1900 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001901 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08001902 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
1903 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1904 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
1905 List<ExpandableNotificationRow> notificationChildren =
1906 row.getNotificationChildren();
1907 for (int childIndex = 0; childIndex < notificationChildren.size();
1908 childIndex++) {
1909 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
1910 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
1911 return rowChild;
1912 }
1913 }
1914 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001915 }
1916 }
1917 return null;
1918 }
1919
1920 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02001921 * @return the last child which has visibility unequal to GONE
1922 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001923 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02001924 int childCount = getChildCount();
1925 for (int i = childCount - 1; i >= 0; i--) {
1926 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001927 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02001928 return child;
1929 }
1930 }
1931 return null;
1932 }
1933
Jorim Jaggi069cd032014-05-15 03:09:01 +02001934 /**
1935 * @return the number of children which have visibility unequal to GONE
1936 */
1937 public int getNotGoneChildCount() {
1938 int childCount = getChildCount();
1939 int count = 0;
1940 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07001941 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001942 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02001943 count++;
1944 }
1945 }
1946 return count;
1947 }
1948
Jorim Jaggi30c305c2014-07-01 23:34:41 +02001949 public int getContentHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001950 return mContentHeight;
1951 }
1952
1953 private void updateContentHeight() {
1954 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08001955 float previousPaddingRequest = mPaddingBetweenElements;
1956 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07001957 int numShownItems = 0;
1958 boolean finish = false;
Adrian Roos7a9551a2017-01-11 12:27:49 -08001959 int maxDisplayedNotifications = mAmbientState.isDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07001960 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08001961 : mMaxDisplayedNotifications;
1962
Selim Cinek67b22602014-03-10 15:40:16 +01001963 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08001964 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Selim Cinek281c2022016-10-13 19:14:43 -07001965 if (expandableView.getVisibility() != View.GONE
1966 && !expandableView.hasNoContentHeight()) {
Adrian Roos7d062c42017-03-30 15:11:43 -07001967 boolean limitReached = maxDisplayedNotifications != -1
1968 && numShownItems >= maxDisplayedNotifications;
Selim Cinekbe2c4432017-05-30 12:11:09 -07001969 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isDark()
1970 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07001971 && expandableView instanceof ExpandableNotificationRow
1972 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
1973 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07001974 expandableView = mShelf;
1975 finish = true;
1976 }
Selim Cinek42357e02016-02-24 18:48:01 -08001977 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08001978 float padding;
1979 if (increasedPaddingAmount >= 0.0f) {
1980 padding = (int) NotificationUtils.interpolate(
1981 previousPaddingRequest,
1982 mIncreasedPaddingBetweenElements,
1983 increasedPaddingAmount);
1984 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08001985 mPaddingBetweenElements,
1986 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08001987 increasedPaddingAmount);
1988 } else {
1989 int ownPadding = (int) NotificationUtils.interpolate(
1990 0,
1991 mPaddingBetweenElements,
1992 1.0f + increasedPaddingAmount);
1993 if (previousPaddingAmount > 0.0f) {
1994 padding = (int) NotificationUtils.interpolate(
1995 ownPadding,
1996 mIncreasedPaddingBetweenElements,
1997 previousPaddingAmount);
1998 } else {
1999 padding = ownPadding;
2000 }
2001 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002002 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002003 if (height != 0) {
2004 height += padding;
2005 }
2006 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002007 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002008 numShownItems++;
2009 if (finish) {
2010 break;
2011 }
Selim Cinek67b22602014-03-10 15:40:16 +01002012 }
2013 }
Anthony Chen9fe1ee72017-04-07 13:53:37 -07002014 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002015 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002016 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002017 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002018 }
2019
Adrian Roos7d062c42017-03-30 15:11:43 -07002020 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002021 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002022 }
2023
Selim Cinekbe2c4432017-05-30 12:11:09 -07002024 public boolean hasPulsingNotifications() {
Adrian Roos7d062c42017-03-30 15:11:43 -07002025 return mPulsing != null;
2026 }
2027
Selim Cinekc22fff62016-05-20 12:44:30 -07002028 private void updateScrollability() {
2029 boolean scrollable = getScrollRange() > 0;
2030 if (scrollable != mScrollable) {
2031 mScrollable = scrollable;
2032 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002033 updateForwardAndBackwardScrollability();
2034 }
2035 }
2036
2037 private void updateForwardAndBackwardScrollability() {
2038 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2039 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2040 boolean changed = forwardScrollable != mForwardScrollable
2041 || backwardsScrollable != mBackwardScrollable;
2042 mForwardScrollable = forwardScrollable;
2043 mBackwardScrollable = backwardsScrollable;
2044 if (changed) {
2045 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002046 }
Selim Cinek67b22602014-03-10 15:40:16 +01002047 }
2048
Selim Cinek6811d722016-01-19 17:53:12 -08002049 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002050 // No need to update the background color if it's not being drawn.
2051 if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002052 return;
2053 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002054
Selim Cinek6811d722016-01-19 17:53:12 -08002055 updateBackgroundBounds();
2056 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002057 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2058 || areBoundsAnimating();
2059 if (!isExpanded()) {
2060 abortBackgroundAnimators();
2061 animate = false;
2062 }
2063 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002064 startBackgroundAnimation();
2065 } else {
2066 mCurrentBounds.set(mBackgroundBounds);
2067 applyCurrentBackgroundBounds();
2068 }
2069 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002070 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002071 }
Selim Cinek614576e2016-01-20 10:54:09 -08002072 mAnimateNextBackgroundBottom = false;
2073 mAnimateNextBackgroundTop = false;
2074 }
2075
Selim Cinek54680902016-10-19 16:49:44 -07002076 private void abortBackgroundAnimators() {
2077 if (mBottomAnimator != null) {
2078 mBottomAnimator.cancel();
2079 }
2080 if (mTopAnimator != null) {
2081 mTopAnimator.cancel();
2082 }
2083 }
2084
Selim Cinek614576e2016-01-20 10:54:09 -08002085 private boolean areBoundsAnimating() {
2086 return mBottomAnimator != null || mTopAnimator != null;
2087 }
2088
2089 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002090 // left and right are always instantly applied
2091 mCurrentBounds.left = mBackgroundBounds.left;
2092 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002093 startBottomAnimation();
2094 startTopAnimation();
2095 }
2096
2097 private void startTopAnimation() {
2098 int previousEndValue = mEndAnimationRect.top;
2099 int newEndValue = mBackgroundBounds.top;
2100 ObjectAnimator previousAnimator = mTopAnimator;
2101 if (previousAnimator != null && previousEndValue == newEndValue) {
2102 return;
2103 }
2104 if (!mAnimateNextBackgroundTop) {
2105 // just a local update was performed
2106 if (previousAnimator != null) {
2107 // we need to increase all animation keyframes of the previous animator by the
2108 // relative change to the end value
2109 int previousStartValue = mStartAnimationRect.top;
2110 PropertyValuesHolder[] values = previousAnimator.getValues();
2111 values[0].setIntValues(previousStartValue, newEndValue);
2112 mStartAnimationRect.top = previousStartValue;
2113 mEndAnimationRect.top = newEndValue;
2114 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2115 return;
2116 } else {
2117 // no new animation needed, let's just apply the value
2118 setBackgroundTop(newEndValue);
2119 return;
2120 }
2121 }
2122 if (previousAnimator != null) {
2123 previousAnimator.cancel();
2124 }
2125 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2126 mCurrentBounds.top, newEndValue);
2127 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2128 animator.setInterpolator(interpolator);
2129 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2130 // remove the tag when the animation is finished
2131 animator.addListener(new AnimatorListenerAdapter() {
2132 @Override
2133 public void onAnimationEnd(Animator animation) {
2134 mStartAnimationRect.top = -1;
2135 mEndAnimationRect.top = -1;
2136 mTopAnimator = null;
2137 }
2138 });
2139 animator.start();
2140 mStartAnimationRect.top = mCurrentBounds.top;
2141 mEndAnimationRect.top = newEndValue;
2142 mTopAnimator = animator;
2143 }
2144
2145 private void startBottomAnimation() {
2146 int previousStartValue = mStartAnimationRect.bottom;
2147 int previousEndValue = mEndAnimationRect.bottom;
2148 int newEndValue = mBackgroundBounds.bottom;
2149 ObjectAnimator previousAnimator = mBottomAnimator;
2150 if (previousAnimator != null && previousEndValue == newEndValue) {
2151 return;
2152 }
2153 if (!mAnimateNextBackgroundBottom) {
2154 // just a local update was performed
2155 if (previousAnimator != null) {
2156 // we need to increase all animation keyframes of the previous animator by the
2157 // relative change to the end value
2158 PropertyValuesHolder[] values = previousAnimator.getValues();
2159 values[0].setIntValues(previousStartValue, newEndValue);
2160 mStartAnimationRect.bottom = previousStartValue;
2161 mEndAnimationRect.bottom = newEndValue;
2162 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2163 return;
2164 } else {
2165 // no new animation needed, let's just apply the value
2166 setBackgroundBottom(newEndValue);
2167 return;
2168 }
2169 }
2170 if (previousAnimator != null) {
2171 previousAnimator.cancel();
2172 }
2173 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2174 mCurrentBounds.bottom, newEndValue);
2175 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2176 animator.setInterpolator(interpolator);
2177 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2178 // remove the tag when the animation is finished
2179 animator.addListener(new AnimatorListenerAdapter() {
2180 @Override
2181 public void onAnimationEnd(Animator animation) {
2182 mStartAnimationRect.bottom = -1;
2183 mEndAnimationRect.bottom = -1;
2184 mBottomAnimator = null;
2185 }
2186 });
2187 animator.start();
2188 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2189 mEndAnimationRect.bottom = newEndValue;
2190 mBottomAnimator = animator;
2191 }
2192
2193 private void setBackgroundTop(int top) {
2194 mCurrentBounds.top = top;
2195 applyCurrentBackgroundBounds();
2196 }
2197
2198 public void setBackgroundBottom(int bottom) {
2199 mCurrentBounds.bottom = bottom;
2200 applyCurrentBackgroundBounds();
2201 }
2202
2203 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002204 // If the background of the notification is not being drawn, then there is no need to
2205 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2206 if (!mShouldDrawNotificationBackground) {
2207 return;
2208 }
2209
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002210 mScrimController.setExcludedBackgroundArea(
Selim Cinek48ff9b42016-11-09 19:31:51 -08002211 mFadingOut || mParentNotFullyVisible || mAmbientState.isDark() || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002212 : mCurrentBounds);
Selim Cinek614576e2016-01-20 10:54:09 -08002213 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002214 }
2215
2216 /**
2217 * Update the background bounds to the new desired bounds
2218 */
2219 private void updateBackgroundBounds() {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01002220 if (mAmbientState.isPanelFullWidth()) {
2221 mBackgroundBounds.left = 0;
2222 mBackgroundBounds.right = getWidth();
2223 } else {
2224 getLocationInWindow(mTempInt2);
2225 mBackgroundBounds.left = mTempInt2[0];
2226 mBackgroundBounds.right = mTempInt2[0] + getWidth();
2227 }
Selim Cinek614576e2016-01-20 10:54:09 -08002228 if (!mIsExpanded) {
2229 mBackgroundBounds.top = 0;
2230 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002231 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002232 }
2233 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002234 int top = 0;
2235 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002236 // Round Y up to avoid seeing the background during animation
2237 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002238 if (mAnimateNextBackgroundTop
2239 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2240 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002241 // we're ending up at the same location as we are now, lets just skip the animation
2242 top = finalTranslationY;
2243 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002244 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002245 }
2246 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002247 ActivatableNotificationView lastView =
2248 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2249 ? mShelf
2250 : mLastVisibleBackgroundChild;
2251 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002252 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002253 int finalTranslationY;
2254 if (lastView == mShelf) {
2255 finalTranslationY = (int) mShelf.getTranslationY();
2256 } else {
2257 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2258 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002259 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002260 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek6811d722016-01-19 17:53:12 -08002261 finalBottom = Math.min(finalBottom, getHeight());
Selim Cinek614576e2016-01-20 10:54:09 -08002262 if (mAnimateNextBackgroundBottom
2263 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2264 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002265 // we're ending up at the same location as we are now, lets just skip the animation
2266 bottom = finalBottom;
2267 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002268 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2269 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002270 bottom = Math.min(bottom, getHeight());
2271 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002272 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002273 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002274 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002275 }
Selim Cinek355652a2016-12-07 13:32:12 -08002276 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002277 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002278 } else {
2279 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002280 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002281 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002282 mBackgroundBounds.top = top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002283 mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
Selim Cinek6811d722016-01-19 17:53:12 -08002284 }
2285
Selim Cinek614576e2016-01-20 10:54:09 -08002286 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2287 int childCount = getChildCount();
2288 for (int i = 0; i < childCount; i++) {
2289 View child = getChildAt(i);
2290 if (child.getVisibility() != View.GONE
2291 && child instanceof ExpandableNotificationRow) {
2292 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2293 if (row.isPinned()) {
2294 return row;
2295 }
2296 }
2297 }
2298 return null;
2299 }
2300
2301 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002302 int childCount = getChildCount();
2303 for (int i = childCount - 1; i >= 0; i--) {
2304 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002305 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2306 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002307 return (ActivatableNotificationView) child;
2308 }
2309 }
2310 return null;
2311 }
2312
Selim Cinek614576e2016-01-20 10:54:09 -08002313 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002314 int childCount = getChildCount();
2315 for (int i = 0; i < childCount; i++) {
2316 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002317 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2318 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002319 return (ActivatableNotificationView) child;
2320 }
2321 }
2322 return null;
2323 }
2324
Selim Cinek67b22602014-03-10 15:40:16 +01002325 /**
2326 * Fling the scroll view
2327 *
2328 * @param velocityY The initial velocity in the Y direction. Positive
2329 * numbers mean that the finger/cursor is moving down the screen,
2330 * which means we want to scroll towards the top.
2331 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002332 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002333 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002334 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002335
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002336 float topAmount = getCurrentOverScrollAmount(true);
2337 float bottomAmount = getCurrentOverScrollAmount(false);
2338 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002339 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002340 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002341 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002342 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002343 * mOverflingDistance + topAmount;
2344 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002345 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002346 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002347 mMaxOverScroll = Math.abs(velocityY) / 1000f
2348 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2349 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002350 } else {
2351 // it will be set once we reach the boundary
2352 mMaxOverScroll = 0.0f;
2353 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002354 int minScrollY = Math.max(0, scrollRange);
2355 if (mExpandedInThisMotion) {
2356 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2357 }
Selim Cinek9212de82017-02-06 16:04:28 -08002358 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2359 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002360
Selim Cinek9212de82017-02-06 16:04:28 -08002361 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002362 }
2363 }
2364
Selim Cinek1408eb52014-06-02 14:45:38 +02002365 /**
2366 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2367 * overScroll view (i.e QS).
2368 */
2369 private boolean shouldOverScrollFling(int initialVelocity) {
2370 float topOverScroll = getCurrentOverScrollAmount(true);
2371 return mScrolledToTopOnFirstDown
2372 && !mExpandedInThisMotion
2373 && topOverScroll > mMinTopOverScrollToEscape
2374 && initialVelocity > 0;
2375 }
2376
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002377 /**
2378 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2379 * account.
2380 *
2381 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002382 * @param animate whether to animate the change
2383 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2384 * {@code qsHeight} is the final top padding
2385 */
Jason Monk16ac3772016-02-10 15:39:21 -05002386 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002387 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002388 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002389 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002390 if (topPadding + minStackHeight > getHeight()) {
2391 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002392 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002393 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002394 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002395 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002396 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002397 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002398 }
2399
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002400 public int getLayoutMinHeight() {
Anthony Chen9e05d462017-04-07 10:10:21 -07002401 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002402 }
2403
Selim Cineka981d082016-08-03 14:45:31 -07002404 public int getFirstChildIntrinsicHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002405 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002406 int firstChildMinHeight = firstChild != null
2407 ? firstChild.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002408 : mEmptyShadeView != null
Selim Cineka981d082016-08-03 14:45:31 -07002409 ? mEmptyShadeView.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002410 : mCollapsedSize;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002411 if (mOwnScrollY > 0) {
2412 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2413 }
Selim Cinek94c2d822016-07-13 18:50:04 -07002414 return firstChildMinHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002415 }
2416
2417 public float getTopPaddingOverflow() {
2418 return mTopPaddingOverflow;
2419 }
2420
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002421 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002422 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002423 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002424 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002425 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002426 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002427 shelfHeight = mShelf.getIntrinsicHeight();
2428 }
2429 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002430 }
2431
Selim Cinek1408eb52014-06-02 14:45:38 +02002432 private int clampPadding(int desiredPadding) {
2433 return Math.max(desiredPadding, mIntrinsicPadding);
2434 }
2435
Selim Cinekfed1ab62014-06-17 14:10:33 -07002436 private float getRubberBandFactor(boolean onTop) {
2437 if (!onTop) {
2438 return RUBBER_BAND_FACTOR_NORMAL;
2439 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002440 if (mExpandedInThisMotion) {
2441 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002442 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002443 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2444 } else if (mScrolledToTopOnFirstDown) {
2445 return 1.0f;
2446 }
2447 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002448 }
2449
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002450 /**
2451 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2452 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2453 * overscroll view (e.g. expand QS).
2454 */
2455 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002456 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002457 || !mScrolledToTopOnFirstDown;
2458 }
2459
Selim Cinek67b22602014-03-10 15:40:16 +01002460 private void endDrag() {
2461 setIsBeingDragged(false);
2462
2463 recycleVelocityTracker();
2464
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002465 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2466 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2467 }
2468 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2469 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2470 }
Selim Cinek67b22602014-03-10 15:40:16 +01002471 }
2472
Jorim Jaggi56306252014-07-03 00:40:09 +02002473 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2474 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2475 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2476 }
2477
Selim Cinek67b22602014-03-10 15:40:16 +01002478 @Override
2479 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002480 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002481 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002482 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002483 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002484 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2485 }
Selim Cinek67b22602014-03-10 15:40:16 +01002486 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002487 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002488 scrollWantsIt = onInterceptTouchEventScroll(ev);
2489 }
2490 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002491 if (!mIsBeingDragged
2492 && !mExpandingNotification
2493 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002494 && !mOnlyScrollingInThisMotion
2495 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002496 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2497 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002498 // Check if we need to clear any snooze leavebehinds
2499 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
2500 NotificationGuts guts = mStatusBar.getExposedGuts();
2501 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2502 && !scrollWantsIt) {
2503 mCheckForLeavebehind = false;
2504 mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
2505 false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
2506 }
2507 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2508 mCheckForLeavebehind = true;
2509 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002510 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2511 }
2512
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002513 private void handleEmptySpaceClick(MotionEvent ev) {
2514 switch (ev.getActionMasked()) {
2515 case MotionEvent.ACTION_MOVE:
2516 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2517 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2518 mTouchIsClick = false;
2519 }
2520 break;
2521 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002522 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002523 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2524 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2525 }
2526 break;
2527 }
2528 }
2529
Selim Cinek1408eb52014-06-02 14:45:38 +02002530 private void initDownStates(MotionEvent ev) {
2531 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2532 mExpandedInThisMotion = false;
2533 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002534 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002535 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002536 mTouchIsClick = true;
2537 mInitialTouchX = ev.getX();
2538 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002539 }
Selim Cinek67b22602014-03-10 15:40:16 +01002540 }
2541
Selim Cinekef5127e2015-12-21 16:55:58 -08002542 public void setChildTransferInProgress(boolean childTransferInProgress) {
2543 mChildTransferInProgress = childTransferInProgress;
2544 }
2545
Christoph Studer068f5922014-04-08 17:43:07 -04002546 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002547 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002548 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002549 // we only call our internal methods if this is actually a removal and not just a
2550 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002551 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002552 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002553 }
2554 }
2555
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002556 /**
2557 * Called when a notification is removed from the shade. This cleans up the state for a given
2558 * view.
2559 */
2560 public void cleanUpViewState(View child) {
2561 if (child == mTranslatingParentView) {
2562 mTranslatingParentView = null;
2563 }
2564 mCurrentStackScrollState.removeViewStateForView(child);
2565 }
2566
Selim Cinek197823d2016-03-24 13:06:00 -07002567 @Override
2568 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2569 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2570 if (disallowIntercept) {
2571 mSwipeHelper.removeLongPressCallback();
2572 }
2573 }
2574
Selim Cineka5703182016-05-11 21:23:16 -04002575 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002576 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002577 // This is only a position change, don't do anything special
2578 return;
2579 }
Selim Cinekd1395642016-04-28 12:22:42 -07002580 ExpandableView expandableView = (ExpandableView) child;
2581 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002582 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002583 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002584 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002585 if (animationGenerated) {
2586 if (!mSwipedOutViews.contains(child)) {
Selim Cineka5703182016-05-11 21:23:16 -04002587 container.getOverlay().add(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002588 } else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002589 container.addTransientView(child, 0);
2590 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002591 }
2592 } else {
2593 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002594 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002595 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002596
Selim Cineke9bad242016-06-15 11:46:37 -07002597 focusNextViewIfFocused(child);
2598 }
2599
2600 private void focusNextViewIfFocused(View view) {
2601 if (view instanceof ExpandableNotificationRow) {
2602 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2603 if (row.shouldRefocusOnDismiss()) {
2604 View nextView = row.getChildAfterViewWhenDismissed();
2605 if (nextView == null) {
2606 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2607 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2608 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002609 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002610 }
2611 if (nextView != null) {
2612 nextView.requestAccessibilityFocus();
2613 }
2614 }
2615 }
2616
Selim Cinekc27437b2014-05-14 10:23:33 +02002617 }
2618
Selim Cinekb5605e52015-02-20 18:21:41 +01002619 private boolean isChildInGroup(View child) {
2620 return child instanceof ExpandableNotificationRow
2621 && mGroupManager.isChildInGroupWithSummary(
2622 ((ExpandableNotificationRow) child).getStatusBarNotification());
2623 }
2624
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002625 /**
2626 * Generate a remove animation for a child view.
2627 *
2628 * @param child The view to generate the remove animation for.
2629 * @return Whether an animation was generated.
2630 */
2631 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002632 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002633 mAddedHeadsUpChildren.remove(child);
2634 return false;
2635 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002636 if (isClickedHeadsUp(child)) {
2637 // An animation is already running, add it to the Overlay
2638 mClearOverlayViewsWhenFinished.add(child);
2639 return true;
2640 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002641 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002642 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002643 // Generate Animations
2644 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002645 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002646 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002647 } else {
2648 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002649 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002650 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002651 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002652 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002653 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002654 }
2655
Selim Cinek0fccc722015-07-29 17:04:36 -07002656 private boolean isClickedHeadsUp(View child) {
2657 return HeadsUpManager.isClickedHeadsUpNotification(child);
2658 }
2659
Selim Cineke0890e52015-06-17 11:17:08 -07002660 /**
2661 * Remove a removed child view from the heads up animations if it was just added there
2662 *
2663 * @return whether any child was removed from the list to animate
2664 */
2665 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2666 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002667 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2668 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002669 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002670 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002671 mTmpList.add(eventPair);
2672 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002673 }
2674 }
Selim Cineke0890e52015-06-17 11:17:08 -07002675 if (hasAddEvent) {
2676 // This child was just added lets remove all events.
2677 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002678 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002679 }
2680 mTmpList.clear();
2681 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002682 }
2683
Selim Cinek572bbd42014-04-25 16:43:27 +02002684 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002685 * @param child the child to query
2686 * @return whether a view is not a top level child but a child notification and that group is
2687 * not expanded
2688 */
2689 private boolean isChildInInvisibleGroup(View child) {
2690 if (child instanceof ExpandableNotificationRow) {
2691 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2692 ExpandableNotificationRow groupSummary =
2693 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2694 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002695 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002696 }
2697 }
2698 return false;
2699 }
2700
2701 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002702 * Updates the scroll position when a child was removed
2703 *
2704 * @param removedChild the removed child
2705 */
Selim Cinek61633a82016-01-25 15:54:10 -08002706 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002707 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002708 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2709 int padding;
2710 if (increasedPaddingAmount >= 0) {
2711 padding = (int) NotificationUtils.interpolate(
2712 mPaddingBetweenElements,
2713 mIncreasedPaddingBetweenElements,
2714 increasedPaddingAmount);
2715 } else {
2716 padding = (int) NotificationUtils.interpolate(
2717 0,
2718 mPaddingBetweenElements,
2719 1.0f + increasedPaddingAmount);
2720 }
Selim Cinek61633a82016-01-25 15:54:10 -08002721 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002722 int endPosition = startingPosition + childHeight;
2723 if (endPosition <= mOwnScrollY) {
2724 // This child is fully scrolled of the top, so we have to deduct its height from the
2725 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002726 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002727 } else if (startingPosition < mOwnScrollY) {
2728 // This child is currently being scrolled into, set the scroll position to the start of
2729 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002730 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002731 }
2732 }
2733
Selim Cinekd7c4e002014-07-04 18:36:42 +02002734 private int getIntrinsicHeight(View view) {
2735 if (view instanceof ExpandableView) {
2736 ExpandableView expandableView = (ExpandableView) view;
2737 return expandableView.getIntrinsicHeight();
2738 }
2739 return view.getHeight();
2740 }
2741
Selim Cinek1f624952017-06-08 19:11:50 -07002742 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002743 ExpandableNotificationRow childInGroup = null;
2744 ExpandableNotificationRow requestedRow = null;
2745 if (isChildInGroup(requestedView)) {
2746 // We're asking for a child in a group. Calculate the position of the parent first,
2747 // then within the parent.
2748 childInGroup = (ExpandableNotificationRow) requestedView;
2749 requestedView = requestedRow = childInGroup.getNotificationParent();
2750 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002751 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002752 float previousPaddingRequest = mPaddingBetweenElements;
2753 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002754 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002755 ExpandableView child = (ExpandableView) getChildAt(i);
2756 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002757 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002758 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002759 float padding;
2760 if (increasedPaddingAmount >= 0.0f) {
2761 padding = (int) NotificationUtils.interpolate(
2762 previousPaddingRequest,
2763 mIncreasedPaddingBetweenElements,
2764 increasedPaddingAmount);
2765 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002766 mPaddingBetweenElements,
2767 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002768 increasedPaddingAmount);
2769 } else {
2770 int ownPadding = (int) NotificationUtils.interpolate(
2771 0,
2772 mPaddingBetweenElements,
2773 1.0f + increasedPaddingAmount);
2774 if (previousPaddingAmount > 0.0f) {
2775 padding = (int) NotificationUtils.interpolate(
2776 ownPadding,
2777 mIncreasedPaddingBetweenElements,
2778 previousPaddingAmount);
2779 } else {
2780 padding = ownPadding;
2781 }
2782 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08002783 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002784 if (position != 0) {
2785 position += padding;
2786 }
2787 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002788 }
Adrian Roos4a579672016-05-24 16:54:37 -07002789 if (child == requestedView) {
2790 if (requestedRow != null) {
2791 position += requestedRow.getPositionOfChild(childInGroup);
2792 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002793 return position;
2794 }
Selim Cinek61633a82016-01-25 15:54:10 -08002795 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02002796 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002797 }
2798 }
2799 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02002800 }
2801
2802 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002803 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02002804 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002805 onViewAddedInternal(child);
2806 }
2807
Selim Cinek614576e2016-01-20 10:54:09 -08002808 private void updateFirstAndLastBackgroundViews() {
2809 ActivatableNotificationView firstChild = getFirstChildWithBackground();
2810 ActivatableNotificationView lastChild = getLastChildWithBackground();
2811 if (mAnimationsEnabled && mIsExpanded) {
2812 mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
2813 mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
2814 } else {
2815 mAnimateNextBackgroundTop = false;
2816 mAnimateNextBackgroundBottom = false;
2817 }
2818 mFirstVisibleBackgroundChild = firstChild;
2819 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08002820 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek614576e2016-01-20 10:54:09 -08002821 }
2822
Selim Cinekb5605e52015-02-20 18:21:41 +01002823 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002824 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02002825 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00002826 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002827 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02002828 updateChronometerForChild(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002829 }
2830
Selim Cinekd06c41c2015-07-06 14:51:36 -07002831 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002832 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002833 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002834 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07002835 }
2836 }
2837
Selim Cinekd1395642016-04-28 12:22:42 -07002838 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
2839 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01002840 }
2841
2842 public void notifyGroupChildAdded(View row) {
2843 onViewAddedInternal(row);
2844 }
2845
Jorim Jaggi75c95042014-05-16 19:09:59 +02002846 public void setAnimationsEnabled(boolean animationsEnabled) {
2847 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02002848 updateNotificationAnimationStates();
2849 }
2850
2851 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002852 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08002853 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02002854 int childCount = getChildCount();
2855 for (int i = 0; i < childCount; i++) {
2856 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07002857 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02002858 updateAnimationState(running, child);
2859 }
2860 }
2861
Selim Cinek51ae05d2014-09-09 15:51:38 +02002862 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002863 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08002864 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002865 }
2866
2867
Selim Cinekcab4a602014-09-03 14:47:57 +02002868 private void updateAnimationState(boolean running, View child) {
2869 if (child instanceof ExpandableNotificationRow) {
2870 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2871 row.setIconAnimationRunning(running);
2872 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02002873 }
2874
2875 public boolean isAddOrRemoveAnimationPending() {
2876 return mNeedsAnimation
2877 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
2878 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002879 /**
2880 * Generate an animation for an added child view.
2881 *
2882 * @param child The view to be added.
Jorim Jaggif6411742014-08-05 17:10:43 +00002883 * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002884 */
Jorim Jaggif6411742014-08-05 17:10:43 +00002885 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002886 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002887 // Generate Animations
2888 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00002889 if (fromMoreCard) {
2890 mFromMoreCardAdditions.add(child);
2891 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002892 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02002893 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08002894 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07002895 mAddedHeadsUpChildren.add(child);
2896 mChildrenToAddAnimated.remove(child);
2897 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002898 }
2899
2900 /**
2901 * Change the position of child to a new location
2902 *
2903 * @param child the view to change the position for
2904 * @param newIndex the new index
2905 */
2906 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04002907 int currentIndex = indexOfChild(child);
2908 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002909 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08002910 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02002911 removeView(child);
2912 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08002913 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02002914 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04002915 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002916 mChildrenChangingPositions.add(child);
2917 mNeedsAnimation = true;
2918 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002919 }
2920 }
2921
Selim Cinekf4c19962014-05-01 21:55:31 +02002922 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002923 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002924 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002925 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002926 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002927 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07002928 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02002929 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
2930 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002931 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08002932 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08002933 updateViewShadows();
Selim Cinekf4c19962014-05-01 21:55:31 +02002934 } else {
2935 applyCurrentState();
2936 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02002937 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02002938 }
2939
2940 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07002941 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02002942 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002943 generateChildAdditionEvents();
2944 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02002945 generateSnapBackEvents();
2946 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002947 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02002948 generateActivateEvent();
2949 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02002950 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04002951 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02002952 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02002953 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01002954 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02002955 generateAnimateEverythingEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002956 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002957 }
2958
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002959 private void generateHeadsUpAnimationEvents() {
2960 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07002961 ExpandableNotificationRow row = eventPair.first;
2962 boolean isHeadsUp = eventPair.second;
2963 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
2964 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07002965 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07002966 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07002967 type = row.wasJustClicked()
2968 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
2969 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07002970 if (row.isChildInGroup()) {
2971 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07002972 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07002973 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04002974 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08002975 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04002976 if (viewState == null) {
2977 // A view state was never generated for this view, so we don't need to animate
2978 // this. This may happen with notification children.
2979 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07002980 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04002981 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
2982 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
2983 // Our custom add animation
2984 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
2985 } else {
2986 // Normal add animation
2987 type = AnimationEvent.ANIMATION_TYPE_ADD;
2988 }
2989 onBottom = !pinnedAndClosed;
2990 }
Selim Cineka59ecc32015-04-07 10:51:49 -07002991 }
2992 AnimationEvent event = new AnimationEvent(row, type);
2993 event.headsUpFromBottom = onBottom;
2994 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002995 }
2996 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07002997 mAddedHeadsUpChildren.clear();
2998 }
2999
Selim Cinekbbcebde2016-11-09 18:28:20 -08003000 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003001 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3002 return false;
3003 }
3004 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003005 }
3006
Selim Cinekb5605e52015-02-20 18:21:41 +01003007 private void generateGroupExpansionEvent() {
3008 // Generate a group expansion/collapsing event if there is such a group at all
3009 if (mExpandedGroupView != null) {
3010 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3011 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3012 mExpandedGroupView = null;
3013 }
3014 }
3015
Selim Cineka5e211b2014-08-11 17:35:48 +02003016 private void generateViewResizeEvent() {
3017 if (mNeedViewResizeAnimation) {
3018 mAnimationEvents.add(
3019 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3020 }
3021 mNeedViewResizeAnimation = false;
3022 }
3023
Selim Cinekeb973562014-05-02 17:07:49 +02003024 private void generateSnapBackEvents() {
3025 for (View child : mSnappedBackChildren) {
3026 mAnimationEvents.add(new AnimationEvent(child,
3027 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3028 }
3029 mSnappedBackChildren.clear();
3030 }
3031
3032 private void generateDragEvents() {
3033 for (View child : mDragAnimPendingChildren) {
3034 mAnimationEvents.add(new AnimationEvent(child,
3035 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3036 }
3037 mDragAnimPendingChildren.clear();
3038 }
3039
Selim Cinek572bbd42014-04-25 16:43:27 +02003040 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003041 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003042 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003043
3044 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003045 float removedTranslation = child.getTranslationY();
3046 boolean ignoreChildren = true;
3047 if (child instanceof ExpandableNotificationRow) {
3048 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3049 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3050 removedTranslation = row.getTranslationWhenRemoved();
3051 ignoreChildren = false;
3052 }
Selim Cinek51052042017-07-04 12:07:55 +02003053 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003054 }
Selim Cinek51052042017-07-04 12:07:55 +02003055 if (!childWasSwipedOut) {
3056 Rect clipBounds = child.getClipBounds();
3057 childWasSwipedOut = clipBounds.height() == 0;
3058 }
3059 int animationType = childWasSwipedOut
3060 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3061 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3062 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003063 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3064 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003065 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003066 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003067 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003068 mChildrenToRemoveAnimated.clear();
3069 }
3070
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003071 private void generatePositionChangeEvents() {
3072 for (View child : mChildrenChangingPositions) {
3073 mAnimationEvents.add(new AnimationEvent(child,
3074 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3075 }
3076 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003077 if (mGenerateChildOrderChangedEvent) {
3078 mAnimationEvents.add(new AnimationEvent(null,
3079 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3080 mGenerateChildOrderChangedEvent = false;
3081 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003082 }
3083
Selim Cinek572bbd42014-04-25 16:43:27 +02003084 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003085 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003086 if (mFromMoreCardAdditions.contains(child)) {
3087 mAnimationEvents.add(new AnimationEvent(child,
3088 AnimationEvent.ANIMATION_TYPE_ADD,
3089 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3090 } else {
3091 mAnimationEvents.add(new AnimationEvent(child,
3092 AnimationEvent.ANIMATION_TYPE_ADD));
3093 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003094 }
3095 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003096 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003097 }
3098
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003099 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003100 if (mTopPaddingNeedsAnimation) {
3101 mAnimationEvents.add(
3102 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
3103 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003104 mTopPaddingNeedsAnimation = false;
3105 }
3106
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003107 private void generateActivateEvent() {
3108 if (mActivateNeedsAnimation) {
3109 mAnimationEvents.add(
3110 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3111 }
3112 mActivateNeedsAnimation = false;
3113 }
3114
Selim Cinekd9acca52014-09-01 22:33:25 +02003115 private void generateAnimateEverythingEvent() {
3116 if (mEverythingNeedsAnimation) {
3117 mAnimationEvents.add(
3118 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3119 }
3120 mEverythingNeedsAnimation = false;
3121 }
3122
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003123 private void generateDimmedEvent() {
3124 if (mDimmedNeedsAnimation) {
3125 mAnimationEvents.add(
3126 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3127 }
3128 mDimmedNeedsAnimation = false;
3129 }
3130
Jorim Jaggiae441282014-08-01 02:45:18 +02003131 private void generateHideSensitiveEvent() {
3132 if (mHideSensitiveNeedsAnimation) {
3133 mAnimationEvents.add(
3134 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3135 }
3136 mHideSensitiveNeedsAnimation = false;
3137 }
3138
John Spurlockbf370992014-06-17 13:58:31 -04003139 private void generateDarkEvent() {
3140 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003141 AnimationEvent ev = new AnimationEvent(null,
3142 AnimationEvent.ANIMATION_TYPE_DARK,
3143 new AnimationFilter()
3144 .animateDark()
3145 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003146 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3147 mAnimationEvents.add(ev);
Selim Cinek972123d2016-05-03 14:25:58 -07003148 startBackgroundFadeIn();
John Spurlockbf370992014-06-17 13:58:31 -04003149 }
3150 mDarkNeedsAnimation = false;
3151 }
3152
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003153 private void generateGoToFullShadeEvent() {
3154 if (mGoToFullShadeNeedsAnimation) {
3155 mAnimationEvents.add(
3156 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3157 }
3158 mGoToFullShadeNeedsAnimation = false;
3159 }
3160
Selim Cinek67b22602014-03-10 15:40:16 +01003161 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003162 if (!isScrollingEnabled()) {
3163 return false;
3164 }
Selim Cinek67b22602014-03-10 15:40:16 +01003165 /*
3166 * This method JUST determines whether we want to intercept the motion.
3167 * If we return true, onMotionEvent will be called and we do the actual
3168 * scrolling there.
3169 */
3170
3171 /*
3172 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003173 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003174 * motion.
3175 */
3176 final int action = ev.getAction();
3177 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3178 return true;
3179 }
3180
Selim Cinek67b22602014-03-10 15:40:16 +01003181 switch (action & MotionEvent.ACTION_MASK) {
3182 case MotionEvent.ACTION_MOVE: {
3183 /*
3184 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003185 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003186 */
3187
3188 /*
3189 * Locally do absolute value. mLastMotionY is set to the y value
3190 * of the down event.
3191 */
3192 final int activePointerId = mActivePointerId;
3193 if (activePointerId == INVALID_POINTER) {
3194 // If we don't have a valid id, the touch down wasn't on content.
3195 break;
3196 }
3197
3198 final int pointerIndex = ev.findPointerIndex(activePointerId);
3199 if (pointerIndex == -1) {
3200 Log.e(TAG, "Invalid pointerId=" + activePointerId
3201 + " in onInterceptTouchEvent");
3202 break;
3203 }
3204
3205 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003206 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003207 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003208 final int xDiff = Math.abs(x - mDownX);
3209 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003210 setIsBeingDragged(true);
3211 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003212 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003213 initVelocityTrackerIfNotExists();
3214 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003215 }
3216 break;
3217 }
3218
3219 case MotionEvent.ACTION_DOWN: {
3220 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303221 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek67b22602014-03-10 15:40:16 +01003222 if (getChildAtPosition(ev.getX(), y) == null) {
3223 setIsBeingDragged(false);
3224 recycleVelocityTracker();
3225 break;
3226 }
3227
3228 /*
3229 * Remember location of down touch.
3230 * ACTION_DOWN always refers to pointer index 0.
3231 */
3232 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003233 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003234 mActivePointerId = ev.getPointerId(0);
3235
3236 initOrResetVelocityTracker();
3237 mVelocityTracker.addMovement(ev);
3238 /*
3239 * If being flinged and user touches the screen, initiate drag;
3240 * otherwise don't. mScroller.isFinished should be false when
3241 * being flinged.
3242 */
3243 boolean isBeingDragged = !mScroller.isFinished();
3244 setIsBeingDragged(isBeingDragged);
3245 break;
3246 }
3247
3248 case MotionEvent.ACTION_CANCEL:
3249 case MotionEvent.ACTION_UP:
3250 /* Release the drag */
3251 setIsBeingDragged(false);
3252 mActivePointerId = INVALID_POINTER;
3253 recycleVelocityTracker();
3254 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08003255 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01003256 }
3257 break;
3258 case MotionEvent.ACTION_POINTER_UP:
3259 onSecondaryPointerUp(ev);
3260 break;
3261 }
3262
3263 /*
3264 * The only time we want to intercept motion events is if we are in the
3265 * drag mode.
3266 */
3267 return mIsBeingDragged;
3268 }
3269
Muyuan Li333a4fc2016-04-16 17:13:46 -07003270 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3271 return new StackScrollAlgorithm(context);
3272 }
3273
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003274 /**
3275 * @return Whether the specified motion event is actually happening over the content.
3276 */
3277 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003278 return isInContentBounds(event.getY());
3279 }
3280
3281 /**
3282 * @return Whether a y coordinate is inside the content.
3283 */
3284 public boolean isInContentBounds(float y) {
3285 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003286 }
3287
Selim Cinek67b22602014-03-10 15:40:16 +01003288 private void setIsBeingDragged(boolean isDragged) {
3289 mIsBeingDragged = isDragged;
3290 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003291 requestDisallowInterceptTouchEvent(true);
Selim Cinek1408eb52014-06-02 14:45:38 +02003292 removeLongPressCallback();
Selim Cinek67b22602014-03-10 15:40:16 +01003293 }
3294 }
3295
3296 @Override
3297 public void onWindowFocusChanged(boolean hasWindowFocus) {
3298 super.onWindowFocusChanged(hasWindowFocus);
3299 if (!hasWindowFocus) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003300 removeLongPressCallback();
Selim Cinek67b22602014-03-10 15:40:16 +01003301 }
3302 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003303
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003304 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003305 public void clearChildFocus(View child) {
3306 super.clearChildFocus(child);
3307 if (mForcedScroll == child) {
3308 mForcedScroll = null;
3309 }
3310 }
3311
3312 @Override
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003313 public void requestDisallowLongPress() {
3314 removeLongPressCallback();
3315 }
3316
Adrian Roosfa139752016-04-27 09:59:08 -07003317 @Override
3318 public void requestDisallowDismiss() {
3319 mDisallowDismissInThisMotion = true;
3320 }
3321
Selim Cinek1408eb52014-06-02 14:45:38 +02003322 public void removeLongPressCallback() {
3323 mSwipeHelper.removeLongPressCallback();
3324 }
3325
Selim Cinekfab078b2014-03-27 22:45:58 +01003326 @Override
3327 public boolean isScrolledToTop() {
3328 return mOwnScrollY == 0;
3329 }
3330
3331 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003332 public boolean isScrolledToBottom() {
3333 return mOwnScrollY >= getScrollRange();
3334 }
3335
3336 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003337 public View getHostView() {
3338 return this;
3339 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003340
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003341 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003342 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003343 }
3344
Mady Mellorc2dbe492017-03-30 13:22:03 -07003345 public void checkSnoozeLeavebehind() {
3346 if (mCheckForLeavebehind) {
3347 mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
3348 false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
3349 mCheckForLeavebehind = false;
3350 }
3351 }
3352
3353 public void resetCheckSnoozeLeavebehind() {
3354 mCheckForLeavebehind = true;
3355 }
3356
Selim Cinek1685e632014-04-08 02:27:49 +02003357 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003358 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003359 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003360 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02003361 }
3362
3363 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003364 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07003365 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08003366 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003367 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003368 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003369 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003370 clearTemporaryViews();
3371 clearUserLockedViews();
3372 }
3373 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01003374
Selim Cinek5b1591a2017-07-03 17:05:01 +02003375 private void clearUserLockedViews() {
3376 for (int i = 0; i < getChildCount(); i++) {
3377 ExpandableView child = (ExpandableView) getChildAt(i);
3378 if (child instanceof ExpandableNotificationRow) {
3379 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3380 row.setUserLocked(false);
3381 }
3382 }
3383 }
3384
3385 private void clearTemporaryViews() {
3386 // lets make sure nothing is in the overlay / transient anymore
3387 clearTemporaryViews(this);
3388 for (int i = 0; i < getChildCount(); i++) {
3389 ExpandableView child = (ExpandableView) getChildAt(i);
3390 if (child instanceof ExpandableNotificationRow) {
3391 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3392 clearTemporaryViews(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003393 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003394 }
Selim Cinek1685e632014-04-08 02:27:49 +02003395 }
3396
Selim Cineka5703182016-05-11 21:23:16 -04003397 private void clearTemporaryViews(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003398 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003399 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003400 }
Selim Cineka5703182016-05-11 21:23:16 -04003401 if (viewGroup != null) {
3402 viewGroup.getOverlay().clear();
3403 }
Selim Cinekd1395642016-04-28 12:22:42 -07003404 }
3405
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003406 public void onPanelTrackingStarted() {
3407 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003408 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003409 }
3410 public void onPanelTrackingStopped() {
3411 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003412 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003413 }
3414
Selim Cinekb24e0a92015-06-09 20:17:30 -07003415 public void resetScrollPosition() {
3416 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003417 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003418 }
3419
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003420 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003421 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003422 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003423 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003424 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003425 if (!mIsExpanded) {
3426 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003427 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08003428 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003429 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003430 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003431 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003432 }
3433 }
3434
3435 private void updateChronometers() {
3436 int childCount = getChildCount();
3437 for (int i = 0; i < childCount; i++) {
3438 updateChronometerForChild(getChildAt(i));
3439 }
3440 }
3441
3442 private void updateChronometerForChild(View child) {
3443 if (child instanceof ExpandableNotificationRow) {
3444 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3445 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003446 }
Selim Cinek1685e632014-04-08 02:27:49 +02003447 }
3448
Jorim Jaggibe565df2014-04-28 17:51:23 +02003449 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003450 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003451 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003452 updateScrollPositionOnExpandInBottom(view);
3453 clampScrollPosition();
Selim Cinekaef92ef2014-06-06 18:06:04 +02003454 notifyHeightChangeListener(view);
Selim Cinekbc243a92016-09-27 16:35:13 -07003455 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3456 ? (ExpandableNotificationRow) view
3457 : null;
3458 if (row != null && (row == mFirstVisibleBackgroundChild
3459 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3460 updateAlgorithmLayoutMinHeight();
3461 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003462 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003463 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003464 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003465 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003466 }
3467
Selim Cineka5e211b2014-08-11 17:35:48 +02003468 @Override
3469 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003470 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003471 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003472 }
3473
Selim Cinekf7a14c02014-07-07 14:01:46 +02003474 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07003475 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02003476 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003477 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003478 if (row.isSummaryWithChildren()) {
3479 return;
3480 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003481 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003482 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003483 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003484 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003485 }
Selim Cinekdb167372016-11-17 15:41:17 -08003486 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Anthony Chen9e05d462017-04-07 10:10:21 -07003487 if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08003488 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3489 }
3490 if (endPosition > layoutEnd) {
3491 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003492 mDisallowScrollingInThisMotion = true;
3493 }
3494 }
3495 }
3496 }
3497
Jorim Jaggibe565df2014-04-28 17:51:23 +02003498 public void setOnHeightChangedListener(
3499 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3500 this.mOnHeightChangedListener = mOnHeightChangedListener;
3501 }
3502
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003503 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3504 mOnEmptySpaceClickListener = listener;
3505 }
3506
Selim Cinek572bbd42014-04-25 16:43:27 +02003507 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003508 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003509 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003510 runAnimationFinishedRunnables();
Selim Cinek0fccc722015-07-29 17:04:36 -07003511 clearViewOverlays();
Selim Cinek8fc78752016-07-13 14:34:56 -07003512 clearHeadsUpDisappearRunning();
3513 }
3514
3515 private void clearHeadsUpDisappearRunning() {
3516 for (int i = 0; i < getChildCount(); i++) {
3517 View view = getChildAt(i);
3518 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003519 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003520 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003521 if (row.isSummaryWithChildren()) {
3522 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003523 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003524 }
3525 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003526 }
3527 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003528 }
3529
3530 private void clearViewOverlays() {
3531 for (View view : mClearOverlayViewsWhenFinished) {
Selim Cineka5703182016-05-11 21:23:16 -04003532 StackStateAnimator.removeFromOverlay(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003533 }
Selim Cinekc8df0642017-02-23 17:01:04 -08003534 mClearOverlayViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003535 }
3536
3537 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003538 for (Runnable runnable : mAnimationFinishedRunnables) {
3539 runnable.run();
3540 }
3541 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003542 }
3543
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003544 /**
3545 * See {@link AmbientState#setDimmed}.
3546 */
3547 public void setDimmed(boolean dimmed, boolean animate) {
3548 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003549 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003550 mDimmedNeedsAnimation = true;
3551 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003552 animateDimmed(dimmed);
3553 } else {
3554 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003555 }
3556 requestChildrenUpdate();
3557 }
3558
Selim Cinekd35c2792016-01-21 13:20:57 -08003559 private void setDimAmount(float dimAmount) {
3560 mDimAmount = dimAmount;
3561 updateBackgroundDimming();
3562 }
3563
3564 private void animateDimmed(boolean dimmed) {
3565 if (mDimAnimator != null) {
3566 mDimAnimator.cancel();
3567 }
3568 float target = dimmed ? 1.0f : 0.0f;
3569 if (target == mDimAmount) {
3570 return;
3571 }
3572 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3573 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3574 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3575 mDimAnimator.addListener(mDimEndListener);
3576 mDimAnimator.addUpdateListener(mDimUpdateListener);
3577 mDimAnimator.start();
3578 }
3579
Jorim Jaggiae441282014-08-01 02:45:18 +02003580 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3581 if (hideSensitive != mAmbientState.isHideSensitive()) {
3582 int childCount = getChildCount();
3583 for (int i = 0; i < childCount; i++) {
3584 ExpandableView v = (ExpandableView) getChildAt(i);
3585 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3586 }
3587 mAmbientState.setHideSensitive(hideSensitive);
3588 if (animate && mAnimationsEnabled) {
3589 mHideSensitiveNeedsAnimation = true;
3590 mNeedsAnimation = true;
3591 }
3592 requestChildrenUpdate();
3593 }
3594 }
3595
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003596 /**
3597 * See {@link AmbientState#setActivatedChild}.
3598 */
Selim Cineka32ab602014-06-11 15:06:01 +02003599 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003600 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003601 if (mAnimationsEnabled) {
3602 mActivateNeedsAnimation = true;
3603 mNeedsAnimation = true;
3604 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003605 requestChildrenUpdate();
3606 }
3607
Selim Cineka32ab602014-06-11 15:06:01 +02003608 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003609 return mAmbientState.getActivatedChild();
3610 }
3611
Selim Cinek572bbd42014-04-25 16:43:27 +02003612 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003613 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003614 if (mListener != null) {
3615 mListener.onChildLocationsChanged(this);
3616 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003617 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003618 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003619 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003620 updateViewShadows();
3621 }
3622
3623 private void updateViewShadows() {
3624 // we need to work around an issue where the shadow would not cast between siblings when
3625 // their z difference is between 0 and 0.1
3626
3627 // Lefts first sort by Z difference
3628 for (int i = 0; i < getChildCount(); i++) {
3629 ExpandableView child = (ExpandableView) getChildAt(i);
3630 if (child.getVisibility() != GONE) {
3631 mTmpSortedChildren.add(child);
3632 }
3633 }
3634 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3635
3636 // Now lets update the shadow for the views
3637 ExpandableView previous = null;
3638 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3639 ExpandableView expandableView = mTmpSortedChildren.get(i);
3640 float translationZ = expandableView.getTranslationZ();
3641 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3642 float diff = otherZ - translationZ;
3643 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3644 // There is no fake shadow to be drawn
3645 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3646 } else {
3647 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003648 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3649 expandableView.setFakeShadowIntensity(
3650 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003651 previous.getOutlineAlpha(), (int) yLocation,
3652 previous.getOutlineTranslation());
3653 }
3654 previous = expandableView;
3655 }
3656
3657 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003658 }
3659
Lucas Dupine17ce522017-07-17 15:45:06 -07003660 /**
3661 * Update colors of "dismiss" and "empty shade" views.
3662 *
3663 * @param lightTheme True if light theme should be used.
3664 */
3665 public void updateDecorViews(boolean lightTheme) {
3666 if (lightTheme == mUsingLightTheme) {
3667 return;
3668 }
3669 mUsingLightTheme = lightTheme;
3670 Context context = new ContextThemeWrapper(mContext,
3671 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
3672 final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
3673 mDismissView.setTextColor(textColor);
3674 mEmptyShadeView.setTextColor(textColor);
3675 }
3676
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003677 public void goToFullShade(long delay) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003678 if (mDismissView != null) {
3679 mDismissView.setInvisible();
3680 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003681 mEmptyShadeView.setInvisible();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003682 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003683 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003684 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003685 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003686 }
3687
Selim Cinek1408eb52014-06-02 14:45:38 +02003688 public void cancelExpandHelper() {
3689 mExpandHelper.cancel();
3690 }
3691
3692 public void setIntrinsicPadding(int intrinsicPadding) {
3693 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07003694 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02003695 }
3696
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003697 public int getIntrinsicPadding() {
3698 return mIntrinsicPadding;
3699 }
3700
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003701 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003702 * @return the y position of the first notification
3703 */
3704 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003705 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003706 }
3707
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003708 @Override
3709 public boolean shouldDelayChildPressedState() {
3710 return true;
3711 }
3712
Jorim Jaggi457cc352014-06-02 22:47:42 +02003713 /**
John Spurlockbf370992014-06-17 13:58:31 -04003714 * See {@link AmbientState#setDark}.
3715 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003716 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02003717 if (mAmbientState.isDark() == dark) {
3718 return;
3719 }
John Spurlockbf370992014-06-17 13:58:31 -04003720 mAmbientState.setDark(dark);
3721 if (animate && mAnimationsEnabled) {
3722 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003723 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
John Spurlockbf370992014-06-17 13:58:31 -04003724 mNeedsAnimation = true;
Selim Cinek972123d2016-05-03 14:25:58 -07003725 setBackgroundFadeAmount(0.0f);
3726 } else if (!dark) {
3727 setBackgroundFadeAmount(1.0f);
John Spurlockbf370992014-06-17 13:58:31 -04003728 }
3729 requestChildrenUpdate();
Selim Cinek6811d722016-01-19 17:53:12 -08003730 if (dark) {
Selim Cinek6811d722016-01-19 17:53:12 -08003731 mScrimController.setExcludedBackgroundArea(null);
3732 } else {
3733 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08003734 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003735
3736 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003737 updateContentHeight();
3738 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04003739 }
3740
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003741 /**
3742 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
3743 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
3744 * {@link #mAmbientState}'s dark mode is toggled.
3745 */
3746 private void updateWillNotDraw() {
Adrian Roosf0b4f962017-05-25 11:53:11 -07003747 boolean willDraw = !mAmbientState.isDark() && mShouldDrawNotificationBackground || DEBUG;
3748 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003749 }
3750
Selim Cinek972123d2016-05-03 14:25:58 -07003751 private void setBackgroundFadeAmount(float fadeAmount) {
3752 mBackgroundFadeAmount = fadeAmount;
3753 updateBackgroundDimming();
3754 }
3755
3756 public float getBackgroundFadeAmount() {
3757 return mBackgroundFadeAmount;
3758 }
3759
3760 private void startBackgroundFadeIn() {
3761 ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, BACKGROUND_FADE, 0f, 1f);
Adrian Roos28f90c72017-05-08 17:24:26 -07003762 fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
Selim Cinek972123d2016-05-03 14:25:58 -07003763 fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
3764 fadeAnimator.start();
3765 }
3766
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003767 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07003768 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003769 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3770 }
3771 if (screenLocation.y > getBottomMostNotificationBottom()) {
3772 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
3773 }
3774 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
3775 if (child != null) {
3776 return getNotGoneIndex(child);
3777 } else {
3778 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3779 }
3780 }
3781
3782 private int getNotGoneIndex(View child) {
3783 int count = getChildCount();
3784 int notGoneIndex = 0;
3785 for (int i = 0; i < count; i++) {
3786 View v = getChildAt(i);
3787 if (child == v) {
3788 return notGoneIndex;
3789 }
3790 if (v.getVisibility() != View.GONE) {
3791 notGoneIndex++;
3792 }
3793 }
3794 return -1;
3795 }
3796
Anthony Chen5e3742e2017-04-07 14:28:44 -07003797 public void setDismissView(@NonNull DismissView dismissView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003798 int index = -1;
3799 if (mDismissView != null) {
3800 index = indexOfChild(mDismissView);
3801 removeView(mDismissView);
3802 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003803 mDismissView = dismissView;
Selim Cinek01af3342016-02-09 19:25:31 -08003804 addView(mDismissView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003805 }
3806
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003807 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003808 int index = -1;
3809 if (mEmptyShadeView != null) {
3810 index = indexOfChild(mEmptyShadeView);
3811 removeView(mEmptyShadeView);
3812 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003813 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08003814 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003815 }
3816
3817 public void updateEmptyShadeView(boolean visible) {
3818 int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
3819 int newVisibility = visible ? VISIBLE : GONE;
3820 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003821 if (newVisibility != GONE) {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003822 if (mEmptyShadeView.willBeGone()) {
3823 mEmptyShadeView.cancelAnimation();
3824 } else {
3825 mEmptyShadeView.setInvisible();
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003826 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003827 mEmptyShadeView.setVisibility(newVisibility);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003828 mEmptyShadeView.setWillBeGone(false);
3829 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003830 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003831 } else {
Selim Cinek20867102014-12-10 17:09:17 +01003832 Runnable onFinishedRunnable = new Runnable() {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003833 @Override
3834 public void run() {
3835 mEmptyShadeView.setVisibility(GONE);
3836 mEmptyShadeView.setWillBeGone(false);
3837 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003838 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003839 }
Selim Cinek20867102014-12-10 17:09:17 +01003840 };
Selim Cineka9d3efd2016-08-16 14:16:59 -07003841 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek20867102014-12-10 17:09:17 +01003842 mEmptyShadeView.setWillBeGone(true);
3843 mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
3844 } else {
3845 mEmptyShadeView.setInvisible();
3846 onFinishedRunnable.run();
3847 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003848 }
3849 }
3850 }
3851
Dan Sandlereceda3d2014-07-21 15:35:01 -04003852 public void updateDismissView(boolean visible) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003853 if (mDismissView == null) {
3854 return;
3855 }
3856
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003857 int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003858 int newVisibility = visible ? VISIBLE : GONE;
3859 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003860 if (newVisibility != GONE) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003861 if (mDismissView.willBeGone()) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003862 mDismissView.cancelAnimation();
3863 } else {
3864 mDismissView.setInvisible();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003865 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003866 mDismissView.setVisibility(newVisibility);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003867 mDismissView.setWillBeGone(false);
3868 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02003869 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003870 } else {
Selim Cinek7d5f3742014-11-07 18:07:49 +01003871 Runnable dimissHideFinishRunnable = new Runnable() {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003872 @Override
3873 public void run() {
3874 mDismissView.setVisibility(GONE);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003875 mDismissView.setWillBeGone(false);
3876 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02003877 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003878 }
Selim Cinek7d5f3742014-11-07 18:07:49 +01003879 };
Selim Cinek20867102014-12-10 17:09:17 +01003880 if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
Selim Cinek7d5f3742014-11-07 18:07:49 +01003881 mDismissView.setWillBeGone(true);
3882 mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
3883 } else {
3884 dimissHideFinishRunnable.run();
Selim Cinek7d5f3742014-11-07 18:07:49 +01003885 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003886 }
3887 }
3888 }
3889
3890 public void setDismissAllInProgress(boolean dismissAllInProgress) {
3891 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07003892 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07003893 handleDismissAllClipping();
3894 }
3895
3896 private void handleDismissAllClipping() {
3897 final int count = getChildCount();
3898 boolean previousChildWillBeDismissed = false;
3899 for (int i = 0; i < count; i++) {
3900 ExpandableView child = (ExpandableView) getChildAt(i);
3901 if (child.getVisibility() == GONE) {
3902 continue;
3903 }
3904 if (mDismissAllInProgress && previousChildWillBeDismissed) {
3905 child.setMinClipTopAmount(child.getClipTopAmount());
3906 } else {
3907 child.setMinClipTopAmount(0);
3908 }
3909 previousChildWillBeDismissed = canChildBeDismissed(child);
3910 }
Selim Cineka272dfe2015-02-20 18:12:28 +01003911 }
3912
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003913 public boolean isDismissViewNotGone() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003914 return mDismissView != null
3915 && mDismissView.getVisibility() != View.GONE
3916 && !mDismissView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003917 }
3918
3919 public boolean isDismissViewVisible() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003920 return mDismissView != null && mDismissView.isVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003921 }
3922
3923 public int getDismissViewHeight() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003924 return mDismissView == null ? 0 : mDismissView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003925 }
3926
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01003927 public int getEmptyShadeViewHeight() {
3928 return mEmptyShadeView.getHeight();
3929 }
3930
Jorim Jaggie0640dd2014-08-05 23:12:40 +02003931 public float getBottomMostNotificationBottom() {
3932 final int count = getChildCount();
3933 float max = 0;
3934 for (int childIdx = 0; childIdx < count; childIdx++) {
3935 ExpandableView child = (ExpandableView) getChildAt(childIdx);
3936 if (child.getVisibility() == GONE) {
3937 continue;
3938 }
Selim Cineka686b2c2016-10-26 13:58:27 -07003939 float bottom = child.getTranslationY() + child.getActualHeight()
3940 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02003941 if (bottom > max) {
3942 max = bottom;
3943 }
3944 }
Selim Cinekd2281152015-04-10 14:37:46 -07003945 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02003946 }
3947
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003948 public void setStatusBar(StatusBar statusBar) {
3949 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02003950 }
3951
Selim Cinekb5605e52015-02-20 18:21:41 +01003952 public void setGroupManager(NotificationGroupManager groupManager) {
3953 this.mGroupManager = groupManager;
3954 }
3955
Selim Cinekd9acca52014-09-01 22:33:25 +02003956 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01003957 requestAnimateEverything();
3958 }
3959
3960 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02003961 if (mIsExpanded && mAnimationsEnabled) {
3962 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01003963 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02003964 requestChildrenUpdate();
3965 }
3966 }
3967
Selim Cinek04fb2582015-06-02 19:58:09 +02003968 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01003969 int childCount = getChildCount();
3970 for (int i = childCount - 1; i >= 0; i--) {
3971 ExpandableView child = (ExpandableView) getChildAt(i);
3972 if (child.getVisibility() != View.GONE) {
3973 float childTop = child.getY();
3974 if (childTop > touchY) {
3975 // we are above a notification entirely let's abort
3976 return false;
3977 }
Selim Cineka686b2c2016-10-26 13:58:27 -07003978 boolean belowChild = touchY > childTop + child.getActualHeight()
3979 - child.getClipBottomAmount();
Selim Cinekabf60bb2015-02-20 17:36:10 +01003980 if (child == mDismissView) {
3981 if(!belowChild && !mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
3982 touchY - childTop)) {
3983 // We clicked on the dismiss button
3984 return false;
3985 }
3986 } else if (child == mEmptyShadeView) {
3987 // We arrived at the empty shade view, for which we accept all clicks
3988 return true;
3989 } else if (!belowChild){
3990 // We are on a child
3991 return false;
3992 }
3993 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003994 }
Selim Cinek04fb2582015-06-02 19:58:09 +02003995 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003996 }
3997
Selim Cinekb5605e52015-02-20 18:21:41 +01003998 @Override
3999 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004000 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4001 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004002 if (animated) {
4003 mExpandedGroupView = changedRow;
4004 mNeedsAnimation = true;
4005 }
4006 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004007 if (!mGroupExpandedForMeasure) {
4008 onHeightChanged(changedRow, false /* needsAnimation */);
4009 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004010 runAfterAnimationFinished(new Runnable() {
4011 @Override
4012 public void run() {
4013 changedRow.onFinishedExpansionChange();
4014 }
4015 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004016 }
4017
4018 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004019 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004020 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004021 }
4022
Selim Cinekc22fff62016-05-20 12:44:30 -07004023 /** @hide */
4024 @Override
4025 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4026 super.onInitializeAccessibilityEventInternal(event);
4027 event.setScrollable(mScrollable);
4028 event.setScrollX(mScrollX);
4029 event.setScrollY(mOwnScrollY);
4030 event.setMaxScrollX(mScrollX);
4031 event.setMaxScrollY(getScrollRange());
4032 }
4033
4034 @Override
4035 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4036 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004037 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004038 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004039 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004040 info.addAction(
4041 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4042 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4043 }
Selim Cinekef406062016-09-29 17:33:13 -07004044 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004045 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4046 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4047 }
4048 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004049 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4050 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004051 }
4052
4053 /** @hide */
4054 @Override
4055 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4056 if (super.performAccessibilityActionInternal(action, arguments)) {
4057 return true;
4058 }
4059 if (!isEnabled()) {
4060 return false;
4061 }
4062 int direction = -1;
4063 switch (action) {
4064 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4065 // fall through
4066 case android.R.id.accessibilityActionScrollDown:
4067 direction = 1;
4068 // fall through
4069 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4070 // fall through
4071 case android.R.id.accessibilityActionScrollUp:
4072 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004073 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004074 final int targetScrollY = Math.max(0,
4075 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4076 if (targetScrollY != mOwnScrollY) {
4077 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004078 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004079 return true;
4080 }
4081 break;
4082 }
4083 return false;
4084 }
4085
Selim Cinekef5127e2015-12-21 16:55:58 -08004086 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004087 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004088 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004089 }
4090
4091 public void generateChildOrderChangedEvent() {
4092 if (mIsExpanded && mAnimationsEnabled) {
4093 mGenerateChildOrderChangedEvent = true;
4094 mNeedsAnimation = true;
4095 requestChildrenUpdate();
4096 }
4097 }
4098
Selim Cinek684a4422015-04-15 16:18:39 -07004099 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004100 mAnimationFinishedRunnables.add(runnable);
4101 }
4102
4103 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
4104 mHeadsUpManager = headsUpManager;
4105 mAmbientState.setHeadsUpManager(headsUpManager);
4106 }
4107
4108 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004109 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004110 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4111 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004112 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004113 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004114 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004115 requestChildrenUpdate();
4116 }
4117 }
4118
4119 public void setShadeExpanded(boolean shadeExpanded) {
4120 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004121 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004122 }
4123
Selim Cineka59ecc32015-04-07 10:51:49 -07004124 /**
4125 * Set the boundary for the bottom heads up position. The heads up will always be above this
4126 * position.
4127 *
4128 * @param height the height of the screen
4129 * @param bottomBarHeight the height of the bar on the bottom
4130 */
4131 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4132 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4133 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004134 requestChildrenUpdate();
4135 }
4136
4137 public void setTrackingHeadsUp(boolean trackingHeadsUp) {
4138 mTrackingHeadsUp = trackingHeadsUp;
4139 }
4140
Selim Cinekaac93252015-04-14 20:04:12 -07004141 public void setScrimController(ScrimController scrimController) {
4142 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004143 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004144 }
4145
Selim Cinekbbc580b2015-06-03 14:11:03 +02004146 public void forceNoOverlappingRendering(boolean force) {
4147 mForceNoOverlappingRendering = force;
4148 }
4149
4150 @Override
4151 public boolean hasOverlappingRendering() {
4152 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4153 }
4154
Selim Cinek6811d722016-01-19 17:53:12 -08004155 public void setAnimationRunning(boolean animationRunning) {
4156 if (animationRunning != mAnimationRunning) {
4157 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004158 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004159 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004160 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004161 }
4162 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004163 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004164 }
4165 }
4166
Selim Cinek3776fe02016-02-04 13:32:43 -08004167 public boolean isExpanded() {
4168 return mIsExpanded;
4169 }
4170
Adrian Roos7d062c42017-03-30 15:11:43 -07004171 public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
4172 if (mPulsing == null && pulsing == null) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004173 return;
4174 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004175 mPulsing = pulsing;
Selim Cinekebf42342017-07-13 15:46:10 +02004176 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004177 updateNotificationAnimationStates();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004178 updateContentHeight();
4179 notifyHeightChangeListener(mShelf);
Adrian Roosd83e9992017-03-16 15:17:57 -07004180 requestChildrenUpdate();
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004181 }
4182
Selim Cinek07304f5222016-05-19 18:31:36 -07004183 public void setFadingOut(boolean fadingOut) {
4184 if (fadingOut != mFadingOut) {
4185 mFadingOut = fadingOut;
4186 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004187 }
4188 }
4189
Selim Cinek48ff9b42016-11-09 19:31:51 -08004190 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4191 if (mScrimController == null) {
4192 // we're not set up yet.
4193 return;
4194 }
4195 if (parentNotFullyVisible != mParentNotFullyVisible) {
4196 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004197 updateFadingState();
4198 }
4199 }
4200
4201 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004202 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004203 updateSrcDrawing();
4204 }
4205
Selim Cinek31d37b92016-04-26 09:56:42 -07004206 @Override
4207 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4208 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004209 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004210 }
4211
Selim Cinekbc243a92016-09-27 16:35:13 -07004212 public void setQsExpanded(boolean qsExpanded) {
4213 mQsExpanded = qsExpanded;
4214 updateAlgorithmLayoutMinHeight();
4215 }
4216
Selim Cinekef406062016-09-29 17:33:13 -07004217 public void setOwnScrollY(int ownScrollY) {
4218 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004219 // We still want to call the normal scrolled changed for accessibility reasons
4220 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004221 mOwnScrollY = ownScrollY;
4222 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004223 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004224 }
4225 }
4226
Selim Cinek281c2022016-10-13 19:14:43 -07004227 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004228 int index = -1;
4229 if (mShelf != null) {
4230 index = indexOfChild(mShelf);
4231 removeView(mShelf);
4232 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004233 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004234 addView(mShelf, index);
4235 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004236 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004237 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004238 }
4239
4240 public NotificationShelf getNotificationShelf() {
4241 return mShelf;
4242 }
4243
Selim Cinekad7fac02016-10-18 17:09:15 -07004244 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4245 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4246 mMaxDisplayedNotifications = maxDisplayedNotifications;
4247 updateContentHeight();
4248 notifyHeightChangeListener(mShelf);
4249 }
4250 }
4251
Selim Cinek48ff9b42016-11-09 19:31:51 -08004252 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004253 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004254 }
4255
Selim Cinekcafa87f2016-10-26 17:00:17 -07004256 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4257 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4258 updateClipping();
4259 }
4260
4261 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4262 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4263 updateClipping();
4264 }
4265
Selim Cinek355652a2016-12-07 13:32:12 -08004266 public void setStatusBarState(int statusBarState) {
4267 mStatusBarState = statusBarState;
4268 mAmbientState.setStatusBarState(statusBarState);
4269 }
4270
Selim Cinekd5ab6452016-12-08 16:34:00 -08004271 public void setExpandingVelocity(float expandingVelocity) {
4272 mAmbientState.setExpandingVelocity(expandingVelocity);
4273 }
4274
Selim Cinekfcff4c62016-12-27 14:26:06 +01004275 public float getOpeningHeight() {
4276 if (mEmptyShadeView.getVisibility() == GONE) {
4277 return getMinExpansionHeight();
4278 } else {
4279 return getAppearEndPosition();
4280 }
4281 }
4282
4283 public void setIsFullWidth(boolean isFullWidth) {
4284 mAmbientState.setPanelFullWidth(isFullWidth);
4285 }
4286
Selim Cinekec29d342017-05-05 18:31:49 -07004287 public void setUnlockHintRunning(boolean running) {
4288 mAmbientState.setUnlockHintRunning(running);
4289 }
4290
Selim Cinek5cf1d052017-06-01 17:36:46 -07004291 public void setQsCustomizerShowing(boolean isShowing) {
4292 mAmbientState.setQsCustomizerShowing(isShowing);
4293 requestChildrenUpdate();
4294 }
4295
4296 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4297 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4298 }
4299
Adrian Roosdc747bd2017-06-01 16:09:15 -07004300 public void setDarkShelfOffsetX(int shelfOffsetX) {
4301 mShelf.setDarkOffsetX(shelfOffsetX);
4302 }
4303
Selim Cinek707e2072017-06-30 18:32:40 +02004304 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4305 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
4306 + " alpha:%f scrollY:%d]",
4307 this.getClass().getSimpleName(),
4308 mPulsing != null ?"T":"f",
4309 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4310 getVisibility() == View.VISIBLE ? "visible"
4311 : getVisibility() == View.GONE ? "gone"
4312 : "invisible",
4313 getAlpha(),
4314 mAmbientState.getScrollY()));
4315 }
4316
Selim Cinek7103fd42016-05-09 22:22:33 -04004317 /**
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004318 * A listener that is notified when some child locations might have changed.
4319 */
4320 public interface OnChildLocationsChangedListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004321 void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout);
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004322 }
Selim Cinek572bbd42014-04-25 16:43:27 +02004323
Jorim Jaggi290600a2014-05-30 17:02:20 +02004324 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004325 * A listener that is notified when the empty space below the notifications is clicked on
4326 */
4327 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004328 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004329 }
4330
4331 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004332 * A listener that gets notified when the overscroll at the top has changed.
4333 */
4334 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004335
4336 /**
4337 * Notifies a listener that the overscroll has changed.
4338 *
4339 * @param amount the amount of overscroll, in pixels
4340 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4341 * unrubberbanded motion to directly expand overscroll view (e.g expand
4342 * QS)
4343 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004344 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004345
4346 /**
4347 * Notify a listener that the scroller wants to escape from the scrolling motion and
4348 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4349 *
4350 * @param velocity The velocity that the Scroller had when over flinging
4351 * @param open Should the fling open or close the overscroll view.
4352 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004353 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004354 }
4355
Mady Mellor95d743c2017-01-10 12:05:27 -08004356 private class NotificationSwipeHelper extends SwipeHelper
4357 implements NotificationSwipeActionHelper {
4358 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004359 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004360 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004361
4362 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4363 super(swipeDirection, callback, context);
4364 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004365 mFalsingCheck = new Runnable() {
4366 @Override
4367 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004368 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004369 }
4370 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004371 }
4372
4373 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004374 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004375 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004376 if (mCurrMenuRow != null) {
4377 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004378 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004379 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004380 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004381
Mady Mellor95d743c2017-01-10 12:05:27 -08004382 // Slide back any notifications that might be showing a menu
4383 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004384
4385 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004386 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
4387 mCurrMenuRow = row.createMenu();
4388 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4389 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor89e15ec2017-06-28 17:08:21 -07004390 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004391 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004392 }
4393
4394 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004395 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004396 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004397 if (mCurrMenuRow != null) {
4398 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004399 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004400 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004401
Mady Mellor95d743c2017-01-10 12:05:27 -08004402 @Override
4403 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4404 float translation) {
4405 if (mCurrMenuRow != null) {
4406 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004407 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004408 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004409 }
4410
4411 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004412 public void dismissChild(final View view, float velocity,
4413 boolean useAccelerateInterpolator) {
4414 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004415 if (mIsExpanded) {
4416 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4417 // of the panel early.
4418 handleChildDismissed(view);
4419 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004420 mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
4421 false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004422 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004423 }
4424
4425 @Override
4426 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004427 super.snapChild(animView, targetLeft, velocity);
4428 onDragCancelled(animView);
4429 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004430 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004431 }
4432 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004433
4434 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004435 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4436 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004437 }
4438
Mady Mellorbd707492017-05-10 17:51:25 -07004439 public boolean isFalseGesture(MotionEvent ev) {
4440 return super.isFalseGesture(ev);
4441 }
4442
Mady Mellor95d743c2017-01-10 12:05:27 -08004443 private void handleMenuCoveredOrDismissed() {
4444 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4445 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004446 }
4447 }
4448
Mady Mellor4b80b102016-01-22 08:03:58 -08004449 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004450 public Animator getViewTranslationAnimator(View v, float target,
4451 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004452 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004453 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4454 } else {
4455 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004456 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004457 }
4458
4459 @Override
4460 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004461 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004462 }
4463
4464 @Override
4465 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004466 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004467 }
4468
Mady Mellor95d743c2017-01-10 12:05:27 -08004469 @Override
4470 public void dismiss(View animView, float velocity) {
4471 dismissChild(animView, velocity,
4472 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4473 }
4474
4475 @Override
4476 public void snap(View animView, float targetLeft, float velocity) {
4477 snapChild(animView, targetLeft, velocity);
4478 }
4479
4480 @Override
4481 public boolean swipedFarEnough(float translation, float viewSize) {
4482 return swipedFarEnough();
4483 }
4484
4485 @Override
4486 public boolean swipedFastEnough(float translation, float velocity) {
4487 return swipedFastEnough();
4488 }
4489
4490 @Override
4491 public float getMinDismissVelocity() {
4492 return getEscapeVelocity();
4493 }
4494
4495 public void onMenuShown(View animView) {
4496 onDragCancelled(animView);
4497
4498 // If we're on the lockscreen we want to false this.
4499 if (isAntiFalsingNeeded()) {
4500 mHandler.removeCallbacks(mFalsingCheck);
4501 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4502 }
4503 }
4504
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004505 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004506 NotificationGuts guts = mStatusBar.getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004507 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004508 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4509 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004510 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004511 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004512 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004513 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004514 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004515 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004516 if (view != null && !isTouchInView(ev, view)) {
4517 // Touch was outside visible guts / menu notification, close what's visible
4518 mStatusBar.closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
4519 true /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
4520 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004521 }
4522 }
4523
Mady Mellor95d743c2017-01-10 12:05:27 -08004524 public void resetExposedMenuView(boolean animate, boolean force) {
4525 if (mMenuExposedView == null
4526 || (!force && mMenuExposedView == mTranslatingParentView)) {
4527 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004528 return;
4529 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004530 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004531 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004532 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004533 0 /* leftTarget */, null /* updateListener */);
4534 if (anim != null) {
4535 anim.start();
4536 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004537 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
4538 ((ExpandableNotificationRow) mMenuExposedView).resetTranslation();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004539 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004540 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004541 }
4542 }
4543
Mady Mellorc2dbe492017-03-30 13:22:03 -07004544 private boolean isTouchInView(MotionEvent ev, View view) {
4545 if (view == null) {
4546 return false;
4547 }
4548 final int height = (view instanceof ExpandableView)
4549 ? ((ExpandableView) view).getActualHeight()
4550 : view.getHeight();
4551 final int rx = (int) ev.getRawX();
4552 final int ry = (int) ev.getRawY();
4553 view.getLocationOnScreen(mTempInt2);
4554 final int x = mTempInt2[0];
4555 final int y = mTempInt2[1];
4556 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4557 boolean ret = rect.contains(rx, ry);
4558 return ret;
4559 }
4560
Selim Cinek33223572016-02-19 19:32:22 -08004561 private void updateContinuousShadowDrawing() {
4562 boolean continuousShadowUpdate = mAnimationRunning
4563 || !mAmbientState.getDraggedViews().isEmpty();
4564 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4565 if (continuousShadowUpdate) {
4566 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4567 } else {
4568 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4569 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004570 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004571 }
4572 }
4573
Mady Mellor95d743c2017-01-10 12:05:27 -08004574 public void resetExposedMenuView(boolean animate, boolean force) {
4575 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004576 }
4577
4578 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4579 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4580 }
4581
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004582 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004583
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004584 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4585
4586 // ANIMATION_TYPE_ADD
4587 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004588 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004589 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004590 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004591 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004592 .animateZ()
4593 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004594
4595 // ANIMATION_TYPE_REMOVE
4596 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004597 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004598 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004599 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004600 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004601 .animateZ()
4602 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004603
4604 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4605 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004606 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004607 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004608 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004609 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004610 .animateZ()
4611 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004612
4613 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4614 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004615 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004616 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004617 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004618 .animateY()
4619 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004620 .animateZ(),
4621
4622 // ANIMATION_TYPE_START_DRAG
4623 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004624 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004625
4626 // ANIMATION_TYPE_SNAP_BACK
4627 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004628 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004629 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004630
4631 // ANIMATION_TYPE_ACTIVATED_CHILD
4632 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004633 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004634
4635 // ANIMATION_TYPE_DIMMED
4636 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004637 .animateDimmed(),
4638
4639 // ANIMATION_TYPE_CHANGE_POSITION
4640 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004641 .animateAlpha() // maybe the children change positions
4642 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004643 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004644 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004645 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004646 .animateZ(),
4647
4648 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004649 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004650
4651 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4652 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004653 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004654 .animateHeight()
4655 .animateTopInset()
4656 .animateY()
4657 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004658 .animateZ()
4659 .hasDelays(),
4660
4661 // ANIMATION_TYPE_HIDE_SENSITIVE
4662 new AnimationFilter()
4663 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02004664
4665 // ANIMATION_TYPE_VIEW_RESIZE
4666 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004667 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02004668 .animateHeight()
4669 .animateTopInset()
4670 .animateY()
4671 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02004672
Selim Cinekb5605e52015-02-20 18:21:41 +01004673 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
4674 new AnimationFilter()
4675 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004676 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01004677 .animateHeight()
4678 .animateTopInset()
4679 .animateY()
4680 .animateZ(),
4681
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004682 // ANIMATION_TYPE_HEADS_UP_APPEAR
4683 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004684 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004685 .animateHeight()
4686 .animateTopInset()
4687 .animateY()
4688 .animateZ(),
4689
4690 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4691 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004692 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004693 .animateHeight()
4694 .animateTopInset()
4695 .animateY()
4696 .animateZ(),
4697
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004698 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4699 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004700 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004701 .animateHeight()
4702 .animateTopInset()
4703 .animateY()
4704 .animateZ()
4705 .hasDelays(),
4706
Selim Cineka59ecc32015-04-07 10:51:49 -07004707 // ANIMATION_TYPE_HEADS_UP_OTHER
4708 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004709 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07004710 .animateHeight()
4711 .animateTopInset()
4712 .animateY()
4713 .animateZ(),
4714
Selim Cinekd9acca52014-09-01 22:33:25 +02004715 // ANIMATION_TYPE_EVERYTHING
4716 new AnimationFilter()
4717 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004718 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02004719 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02004720 .animateDimmed()
4721 .animateHideSensitive()
4722 .animateHeight()
4723 .animateTopInset()
4724 .animateY()
4725 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004726 };
4727
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004728 static int[] LENGTHS = new int[] {
4729
4730 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004731 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004732
4733 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004734 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004735
4736 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4737 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4738
4739 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4740 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4741
4742 // ANIMATION_TYPE_START_DRAG
4743 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4744
4745 // ANIMATION_TYPE_SNAP_BACK
4746 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4747
4748 // ANIMATION_TYPE_ACTIVATED_CHILD
4749 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
4750
4751 // ANIMATION_TYPE_DIMMED
4752 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004753
4754 // ANIMATION_TYPE_CHANGE_POSITION
4755 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04004756
4757 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004758 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004759
4760 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4761 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02004762
4763 // ANIMATION_TYPE_HIDE_SENSITIVE
4764 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02004765
4766 // ANIMATION_TYPE_VIEW_RESIZE
4767 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02004768
Selim Cinekb5605e52015-02-20 18:21:41 +01004769 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08004770 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01004771
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004772 // ANIMATION_TYPE_HEADS_UP_APPEAR
4773 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
4774
4775 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4776 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4777
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004778 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4779 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4780
Selim Cineka59ecc32015-04-07 10:51:49 -07004781 // ANIMATION_TYPE_HEADS_UP_OTHER
4782 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4783
Selim Cinekd9acca52014-09-01 22:33:25 +02004784 // ANIMATION_TYPE_EVERYTHING
4785 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004786 };
4787
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004788 static final int ANIMATION_TYPE_ADD = 0;
4789 static final int ANIMATION_TYPE_REMOVE = 1;
4790 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
4791 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
4792 static final int ANIMATION_TYPE_START_DRAG = 4;
4793 static final int ANIMATION_TYPE_SNAP_BACK = 5;
4794 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
4795 static final int ANIMATION_TYPE_DIMMED = 7;
4796 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04004797 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004798 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02004799 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02004800 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01004801 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004802 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
4803 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004804 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
4805 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
4806 static final int ANIMATION_TYPE_EVERYTHING = 18;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004807
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004808 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
4809 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
4810
Selim Cinek572bbd42014-04-25 16:43:27 +02004811 final long eventStartTime;
4812 final View changingView;
4813 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004814 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004815 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004816 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004817 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07004818 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02004819
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004820 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004821 this(view, type, LENGTHS[type]);
4822 }
4823
Adrian Roos28f90c72017-05-08 17:24:26 -07004824 AnimationEvent(View view, int type, AnimationFilter filter) {
4825 this(view, type, LENGTHS[type], filter);
4826 }
4827
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004828 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07004829 this(view, type, length, FILTERS[type]);
4830 }
4831
4832 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02004833 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
4834 changingView = view;
4835 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004836 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07004837 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004838 }
4839
4840 /**
4841 * Combines the length of several animation events into a single value.
4842 *
4843 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004844 * @return The combined length. Depending on the event types, this might be the maximum of
4845 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004846 */
4847 static long combineLength(ArrayList<AnimationEvent> events) {
4848 long length = 0;
4849 int size = events.size();
4850 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004851 AnimationEvent event = events.get(i);
4852 length = Math.max(length, event.length);
4853 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
4854 return event.length;
4855 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004856 }
4857 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02004858 }
4859 }
Selim Cinek67b22602014-03-10 15:40:16 +01004860}