blob: b1c0a9605e28fa38a6f0beea5526733946162beb [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
Julia Reynoldsed1c9af2018-03-21 15:21:09 -040019import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
20 .ExpandAnimationParameters;
Selim Cinek2627d722018-01-19 12:16:49 -080021
Selim Cinek614576e2016-01-20 10:54:09 -080022import android.animation.Animator;
23import android.animation.AnimatorListenerAdapter;
24import android.animation.ObjectAnimator;
25import android.animation.PropertyValuesHolder;
Selim Cinekd35c2792016-01-21 13:20:57 -080026import android.animation.TimeAnimator;
27import android.animation.ValueAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080028import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinek31d37b92016-04-26 09:56:42 -070029import android.annotation.FloatRange;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010030import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010031import android.content.Context;
32import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080033import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010034import android.graphics.Canvas;
Lucas Dupind285cf02018-01-18 09:18:23 -080035import android.graphics.Color;
Selim Cinek67b22602014-03-10 15:40:16 +010036import android.graphics.Paint;
Selim Cinek515b2032017-11-15 10:20:19 -080037import android.graphics.Path;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010038import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080039import android.graphics.PorterDuff;
40import android.graphics.PorterDuffXfermode;
41import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070042import android.os.Bundle;
Mady Mellor4b80b102016-01-22 08:03:58 -080043import android.os.Handler;
Mady Mellor95d743c2017-01-10 12:05:27 -080044import android.service.notification.StatusBarNotification;
Anthony Chen5e3742e2017-04-07 14:28:44 -070045import android.support.annotation.NonNull;
Selim Cinek8a9308d2017-08-24 09:31:08 -070046import android.support.annotation.VisibleForTesting;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -040047import android.support.v4.graphics.ColorUtils;
Selim Cinek67b22602014-03-10 15:40:16 +010048import android.util.AttributeSet;
Selim Cinek972123d2016-05-03 14:25:58 -070049import android.util.FloatProperty;
Selim Cinek67b22602014-03-10 15:40:16 +010050import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080051import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070052import android.util.Pair;
Selim Cinek972123d2016-05-03 14:25:58 -070053import android.util.Property;
Lucas Dupine17ce522017-07-17 15:45:06 -070054import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070055import android.view.InputDevice;
Selim Cinek67b22602014-03-10 15:40:16 +010056import android.view.MotionEvent;
57import android.view.VelocityTracker;
58import android.view.View;
59import android.view.ViewConfiguration;
60import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020061import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070062import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070063import android.view.accessibility.AccessibilityEvent;
64import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020065import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080066import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010067import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070068import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070069
Mady Mellora41587b2016-02-11 18:43:06 -080070import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010071import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Lucas Dupine17ce522017-07-17 15:45:06 -070072import com.android.settingslib.Utils;
Rohan Shah524cf7b2018-03-15 14:40:02 -070073import com.android.systemui.Dependency;
Selim Cinek67b22602014-03-10 15:40:16 +010074import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080075import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010076import com.android.systemui.R;
77import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070078import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080079import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
80import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
81import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Selim Cineka32ab602014-06-11 15:06:01 +020082import com.android.systemui.statusbar.ActivatableNotificationView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020083import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010084import com.android.systemui.statusbar.ExpandableNotificationRow;
Jorim Jaggibe565df2014-04-28 17:51:23 +020085import com.android.systemui.statusbar.ExpandableView;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -040086import com.android.systemui.statusbar.FooterView;
Rohan Shah524cf7b2018-03-15 14:40:02 -070087import com.android.systemui.statusbar.NotificationBlockingHelperManager;
Adrian Roos7d062c42017-03-30 15:11:43 -070088import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070089import com.android.systemui.statusbar.NotificationGuts;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090090import com.android.systemui.statusbar.NotificationListContainer;
91import com.android.systemui.statusbar.NotificationLogger;
Selim Cinek0cfbef42016-11-09 19:06:36 -080092import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070093import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010094import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020095import com.android.systemui.statusbar.StatusBarState;
Selim Cinek33223572016-02-19 19:32:22 -080096import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080097import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080098import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Lucas Dupinb561eda2018-04-09 17:25:04 -070099import com.android.systemui.statusbar.phone.DozeParameters;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900100import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Selim Cinekb5605e52015-02-20 18:21:41 +0100101import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinekaac93252015-04-14 20:04:12 -0700102import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800103import com.android.systemui.statusbar.phone.StatusBar;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900104import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100105import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +0100106
Selim Cinek707e2072017-06-30 18:32:40 +0200107import java.io.FileDescriptor;
108import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200109import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800110import java.util.Collections;
111import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200112import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800113import java.util.List;
Beverlybadf6e42018-04-18 17:32:56 -0400114import java.util.Objects;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800115import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200116
Selim Cinek67b22602014-03-10 15:40:16 +0100117/**
118 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
119 */
120public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200121 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800122 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900123 NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
124 NotificationListContainer {
Selim Cinek67b22602014-03-10 15:40:16 +0100125
Selim Cinekd35c2792016-01-21 13:20:57 -0800126 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800127 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100128 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200129 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
130 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200131 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100132 /**
133 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
134 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800135 private static final int INVALID_POINTER = -1;
Selim Cinek67b22602014-03-10 15:40:16 +0100136
Selim Cinek1408eb52014-06-02 14:45:38 +0200137 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800138 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200139 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100140 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800141 private final Paint mBackgroundPaint = new Paint();
Selim Cinek515b2032017-11-15 10:20:19 -0800142 private final Path mBackgroundPath = new Path();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700143 private final boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100144
Selim Cinekbc243a92016-09-27 16:35:13 -0700145 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100146 private int mOwnScrollY;
147 private int mMaxLayoutHeight;
148
149 private VelocityTracker mVelocityTracker;
150 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100151 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100152 private int mTouchSlop;
153 private int mMinimumVelocity;
154 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100155 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200156 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100157 private boolean mIsBeingDragged;
158 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200159 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700160 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100161 private boolean mTouchIsClick;
162 private float mInitialTouchX;
163 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100164
Selim Cinek67b22602014-03-10 15:40:16 +0100165 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100166 private int mContentHeight;
Lucas Dupin60661a62018-04-12 10:50:13 -0700167 private int mIntrinsicContentHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100168 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100169 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800170 private int mIncreasedPaddingBetweenElements;
Lucas Dupind285cf02018-01-18 09:18:23 -0800171 private int mRegularTopPadding;
172 private int mDarkTopPadding;
173 // Current padding, will be either mRegularTopPadding or mDarkTopPadding
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200174 private int mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800175 // Distance between AOD separator and shelf
176 private int mDarkSeparatorPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700177 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700178 private int mBottomInset = 0;
Selim Cinek67b22602014-03-10 15:40:16 +0100179
180 /**
181 * The algorithm which calculates the properties for our children
182 */
Muyuan Li87798022016-04-07 17:51:25 -0700183 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100184
185 /**
186 * The current State this Layout is in
187 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200188 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700189 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100190 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800191 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700192 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
193 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
194 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
195 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
196 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200197 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700198 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
199 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200200 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200201 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200202 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800203 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200204
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200205 /**
206 * The raw amount of the overScroll on the top, which is not rubber-banded.
207 */
208 private float mOverScrolledTopPixels;
209
210 /**
211 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
212 */
213 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900214 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200215 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200216 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100217 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200218 private boolean mNeedsAnimation;
219 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200220 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200221 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400222 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100223 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200224 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200225 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200226 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200227 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200228 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700229 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200230 private boolean mExpandingNotification;
231 private boolean mExpandedInThisMotion;
Muyuan Li84b45612016-06-01 11:05:08 -0700232 protected boolean mScrollingEnabled;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400233 protected FooterView mFooterView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700234 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400235 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700236 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200237
238 /**
239 * Was the scroller scrolled to the top when the down motion was observed?
240 */
241 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200242 /**
243 * The minimal amount of over scroll which is needed in order to switch to the quick settings
244 * when over scrolling on a expanded card.
245 */
246 private float mMinTopOverScrollToEscape;
247 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700248 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200249 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200250 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700251 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200252 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100253 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700254 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700255
Selim Cinek1408eb52014-06-02 14:45:38 +0200256 /**
257 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
258 * This is needed to avoid scrolling too far after the notification was collapsed in the same
259 * motion.
260 */
261 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000262 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800263
Mady Mellor95d743c2017-01-10 12:05:27 -0800264 private NotificationMenuRowPlugin mCurrMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800265 private View mTranslatingParentView;
Mady Mellor95d743c2017-01-10 12:05:27 -0800266 private View mMenuExposedView;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700267 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200268
269 /**
270 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
271 * animating.
272 */
273 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700274 private boolean mDisallowDismissInThisMotion;
Selim Cineka59ecc32015-04-07 10:51:49 -0700275 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700276 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200277 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200278 = new ViewTreeObserver.OnPreDrawListener() {
279 @Override
280 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400281 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200282 updateChildren();
283 mChildrenUpdateRequested = false;
284 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200285 return true;
286 }
287 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500288 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100289 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100290 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700291 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek0fccc722015-07-29 17:04:36 -0700292 private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700293 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
294 = new HashSet<>();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900295 private HeadsUpManagerPhone mHeadsUpManager;
Selim Cinek29aab962018-02-27 17:05:45 -0800296 private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700297 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700298 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200299 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700300 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700301 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800302 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700303 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800304 = new ViewTreeObserver.OnPreDrawListener() {
305 @Override
306 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700307 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800308 return true;
309 }
310 };
311 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800312 private Rect mStartAnimationRect = new Rect();
313 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800314 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800315 private boolean mAnimateNextBackgroundBottom;
316 private boolean mAnimateNextBackgroundTop;
317 private ObjectAnimator mBottomAnimator = null;
318 private ObjectAnimator mTopAnimator = null;
319 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
320 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800321 private int mBgColor;
322 private float mDimAmount;
323 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800324 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800325 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
326 @Override
327 public void onAnimationEnd(Animator animation) {
328 mDimAnimator = null;
329 }
330 };
331 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
332 = new ValueAnimator.AnimatorUpdateListener() {
333
334 @Override
335 public void onAnimationUpdate(ValueAnimator animation) {
336 setDimAmount((Float) animation.getAnimatedValue());
337 }
338 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700339 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800340 private boolean mContinuousShadowUpdate;
341 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
342 = new ViewTreeObserver.OnPreDrawListener() {
343
344 @Override
345 public boolean onPreDraw() {
346 updateViewShadows();
347 return true;
348 }
349 };
350 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
351 @Override
352 public int compare(ExpandableView view, ExpandableView otherView) {
353 float endY = view.getTranslationY() + view.getActualHeight();
354 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
355 if (endY < otherEndY) {
356 return -1;
357 } else if (endY > otherEndY) {
358 return 1;
359 } else {
360 // The two notifications end at the same location
361 return 0;
362 }
363 }
364 };
Selim Cinek25503252016-03-03 15:31:43 -0800365 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900366 private boolean mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700367 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700368 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800369 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700370 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700371 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400372 private View mForcedScroll;
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -0800373 private float mDarkAmount = 0f;
Lucas Dupind285cf02018-01-18 09:18:23 -0800374 private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
375 new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
Selim Cinek972123d2016-05-03 14:25:58 -0700376 @Override
377 public void setValue(NotificationStackScrollLayout object, float value) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800378 object.setDarkAmount(value);
Selim Cinek972123d2016-05-03 14:25:58 -0700379 }
380
381 @Override
382 public Float get(NotificationStackScrollLayout object) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800383 return object.getDarkAmount();
Selim Cinek972123d2016-05-03 14:25:58 -0700384 }
385 };
Lucas Dupine17ce522017-07-17 15:45:06 -0700386 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700387 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700388 private boolean mForwardScrollable;
389 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700390 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700391 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800392 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700393 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800394 private boolean mNoAmbient;
395 private final Rect mClipRect = new Rect();
396 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700397 private Rect mRequestedClipBounds;
398 private boolean mInHeadsUpPinnedMode;
399 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800400 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100401 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700402 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800403 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek0fe07392017-11-09 13:26:34 -0800404 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800405 private int mSidePaddings;
Lucas Dupind285cf02018-01-18 09:18:23 -0800406 private final int mSeparatorWidth;
407 private final int mSeparatorThickness;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800408 private final Rect mBackgroundAnimationRect = new Rect();
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800409 private int mClockBottom;
Lucas Dupin0cd882f2018-01-30 12:19:49 -0800410 private int mAntiBurnInOffsetX;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800411 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
412 private int mHeadsUpInset;
Selim Cinek67b22602014-03-10 15:40:16 +0100413
414 public NotificationStackScrollLayout(Context context) {
415 this(context, null);
416 }
417
418 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
419 this(context, attrs, 0);
420 }
421
422 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
423 this(context, attrs, defStyleAttr, 0);
424 }
425
426 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
427 int defStyleRes) {
428 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800429 Resources res = getResources();
430
Selim Cinek281c2022016-10-13 19:14:43 -0700431 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800432 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800433 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
434 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200435 mExpandHelper = new ExpandHelper(getContext(), this,
436 minHeight, maxHeight);
437 mExpandHelper.setEventSource(this);
438 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800439 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Muyuan Li333a4fc2016-04-16 17:13:46 -0700440 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100441 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800442 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800443 mShouldDrawNotificationBackground =
444 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700445 mFadeNotificationsOnDismiss =
446 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Lucas Dupind285cf02018-01-18 09:18:23 -0800447 mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
448 mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
449 mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
Selim Cinek29aab962018-02-27 17:05:45 -0800450 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
451 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800452 addOnExpandedHeightListener(mRoundnessManager::setExpanded);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800453
Rohan Shah524cf7b2018-03-15 14:40:02 -0700454 // Blocking helper manager wants to know the expanded state, update as well.
455 NotificationBlockingHelperManager blockingHelperManager =
456 Dependency.get(NotificationBlockingHelperManager.class);
457 addOnExpandedHeightListener((height, unused) -> {
458 blockingHelperManager.setNotificationShadeExpanded(height);
459 });
460
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800461 updateWillNotDraw();
Lucas Dupind285cf02018-01-18 09:18:23 -0800462 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100463 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100464 mDebugPaint = new Paint();
465 mDebugPaint.setColor(0xffff0000);
466 mDebugPaint.setStrokeWidth(2);
467 mDebugPaint.setStyle(Paint.Style.STROKE);
468 }
469 }
470
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900471 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800472 public NotificationSwipeActionHelper getSwipeActionHelper() {
473 return mSwipeHelper;
474 }
475
Selim Cinek67b22602014-03-10 15:40:16 +0100476 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800477 public void onMenuClicked(View view, int x, int y, MenuItem item) {
478 if (mLongPressListener == null) {
479 return;
480 }
481 if (view instanceof ExpandableNotificationRow) {
482 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800483 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
484 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800485 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800486 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800487 }
488
489 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800490 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700491 if (mTranslatingParentView != null && row == mTranslatingParentView) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800492 mMenuExposedView = null;
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700493 mTranslatingParentView = null;
494 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000495 }
496
497 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800498 public void onMenuShown(View row) {
499 mMenuExposedView = mTranslatingParentView;
500 if (row instanceof ExpandableNotificationRow) {
501 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
502 ((ExpandableNotificationRow) row).getStatusBarNotification()
503 .getPackageName());
504 }
505 mSwipeHelper.onMenuShown(row);
506 }
507
Selim Cinek67b22602014-03-10 15:40:16 +0100508 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800509 if (mShouldDrawNotificationBackground
510 && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
511 drawBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700512 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800513
Selim Cinek67b22602014-03-10 15:40:16 +0100514 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800515 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100516 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800517 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100518 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200519 y = getHeight() - getEmptyBottomMargin();
520 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100521 }
522 }
523
Lucas Dupind285cf02018-01-18 09:18:23 -0800524 private void drawBackground(Canvas canvas) {
525 final int lockScreenLeft = mSidePaddings;
526 final int lockScreenRight = getWidth() - mSidePaddings;
527 final int lockScreenTop = mCurrentBounds.top;
528 final int lockScreenBottom = mCurrentBounds.bottom;
529 final int darkLeft = getWidth() / 2 - mSeparatorWidth / 2;
530 final int darkRight = darkLeft + mSeparatorWidth;
531 final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
532 final int darkBottom = darkTop + mSeparatorThickness;
533
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800534 if (mAmbientState.hasPulsingNotifications()) {
Lucas Dupin16cfe452018-02-08 13:14:50 -0800535 // No divider, we have a notification icon instead
536 } else if (mAmbientState.isFullyDark()) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800537 // Only draw divider on AOD if we actually have notifications
538 if (mFirstVisibleBackgroundChild != null) {
539 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
540 }
Lucas Dupind285cf02018-01-18 09:18:23 -0800541 } else {
Lucas Dupin60661a62018-04-12 10:50:13 -0700542 float inverseDark = 1 - mDarkAmount;
543 float yProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(inverseDark);
544 float xProgress = Interpolators.FAST_OUT_SLOW_IN
545 .getInterpolation(inverseDark * 2f);
546
Lucas Dupin16cfe452018-02-08 13:14:50 -0800547 mBackgroundAnimationRect.set(
Lucas Dupin60661a62018-04-12 10:50:13 -0700548 (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
549 (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
550 (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
551 (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
Lucas Dupin16cfe452018-02-08 13:14:50 -0800552 if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
553 canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
554 mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
555 mCornerRadius, mCornerRadius, mBackgroundPaint);
556 }
Lucas Dupind285cf02018-01-18 09:18:23 -0800557 }
Lucas Dupin16cfe452018-02-08 13:14:50 -0800558 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800559 }
560
Selim Cinekd35c2792016-01-21 13:20:57 -0800561 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800562 // No need to update the background color if it's not being drawn.
563 if (!mShouldDrawNotificationBackground) {
564 return;
565 }
566
Lucas Dupinb561eda2018-04-09 17:25:04 -0700567 float alpha =
568 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
569 alpha *= 1f - mDarkAmount;
570 // We need to manually blend in the background color.
571 int scrimColor = mScrimController.getBackgroundColor();
572 int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
573
574 // Interpolate between semi-transparent notification panel background color
575 // and white AOD separator.
576 float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(mDarkAmount);
577 int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800578
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100579 if (mCachedBackgroundColor != color) {
580 mCachedBackgroundColor = color;
581 mBackgroundPaint.setColor(color);
582 invalidate();
583 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800584 }
585
Selim Cinek67b22602014-03-10 15:40:16 +0100586 private void initView(Context context) {
587 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100588 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200589 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100590 final ViewConfiguration configuration = ViewConfiguration.get(context);
591 mTouchSlop = configuration.getScaledTouchSlop();
592 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
593 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100594 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700595
596 Resources res = context.getResources();
597 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800598 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700599 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700600 mPaddingBetweenElements = Math.max(1,
601 res.getDimensionPixelSize(R.dimen.notification_divider_height));
602 mIncreasedPaddingBetweenElements =
603 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
604 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200605 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800606 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700607 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800608 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700609 mMinInteractionHeight = res.getDimensionPixelSize(
610 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800611 mCornerRadius = res.getDimensionPixelSize(
612 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800613 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
614 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200615 }
616
Selim Cinek25503252016-03-03 15:31:43 -0800617 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700618 mDrawBackgroundAsSrc = asSrc;
619 updateSrcDrawing();
620 }
621
622 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800623 if (!mShouldDrawNotificationBackground) {
624 return;
625 }
626
Selim Cinek48ff9b42016-11-09 19:31:51 -0800627 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700628 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800629 invalidate();
630 }
631
Selim Cinekaef92ef2014-06-06 18:06:04 +0200632 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700633 notifyHeightChangeListener(view, false /* needsAnimation */);
634 }
635
636 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +0200637 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700638 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200639 }
Selim Cinek67b22602014-03-10 15:40:16 +0100640 }
641
642 @Override
643 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
644 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -0800645
646 int width = MeasureSpec.getSize(widthMeasureSpec);
647 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
648 MeasureSpec.getMode(widthMeasureSpec));
Selim Cinekfa760d42016-05-10 15:50:53 -0400649 // We need to measure all children even the GONE ones, such that the heights are calculated
650 // correctly as they are used to calculate how many we can fit on the screen.
651 final int size = getChildCount();
652 for (int i = 0; i < size; i++) {
Selim Cinekb95fd182017-12-21 13:03:32 -0800653 measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400654 }
Selim Cinek67b22602014-03-10 15:40:16 +0100655 }
656
657 @Override
658 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100659 // we layout all our children centered on the top
660 float centerX = getWidth() / 2.0f;
661 for (int i = 0; i < getChildCount(); i++) {
662 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400663 // We need to layout all children even the GONE ones, such that the heights are
664 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100665 float width = child.getMeasuredWidth();
666 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100667 child.layout((int) (centerX - width / 2.0f),
668 0,
669 (int) (centerX + width / 2.0f),
670 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100671 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200672 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100673 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200674 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200675 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800676 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700677 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100678 }
679
Selim Cinek5bc852a2015-12-21 12:19:09 -0800680 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
681 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200682 mNeedViewResizeAnimation = true;
683 mNeedsAnimation = true;
684 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200685 }
686
Selim Cinekdb167372016-11-17 15:41:17 -0800687 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
688 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800689 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200690 }
691
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900692 @Override
693 public void setChildLocationsChangedListener(
694 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200695 mListener = listener;
696 }
697
Selim Cineka7d4f822016-12-06 14:34:47 -0800698 @Override
699 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
700 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200701 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800702 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200703 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100704 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800705 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100706 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800707 if (row.getVisibility() != View.VISIBLE) {
708 return false;
709 }
710 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200711 }
712
Selim Cinek67b22602014-03-10 15:40:16 +0100713 private void setMaxLayoutHeight(int maxLayoutHeight) {
714 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700715 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200716 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100717 }
718
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200719 private void updateAlgorithmHeightAndPadding() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900720 if (mPulsing) {
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800721 mTopPadding = mClockBottom;
722 } else {
Lucas Dupin16cfe452018-02-08 13:14:50 -0800723 mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800724 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700725 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700726 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700727 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100728 }
729
Selim Cinekbc243a92016-09-27 16:35:13 -0700730 private void updateAlgorithmLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800731 mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
732 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -0700733 }
734
Selim Cinek67b22602014-03-10 15:40:16 +0100735 /**
736 * Updates the children views according to the stack scroll algorithm. Call this whenever
737 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
738 */
739 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800740 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800741 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
742 ? 0
743 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200744 mAmbientState.setScrollY(mOwnScrollY);
745 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200746 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200747 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100748 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200749 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100750 }
751 }
752
Selim Cinekc383fd02016-10-21 15:31:26 -0700753 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700754 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800755 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -0700756 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
757 updateBackground();
758 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700759 }
760
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800761 private void updateClippingToTopRoundedCorner() {
Selim Cinek2627d722018-01-19 12:16:49 -0800762 Float clipStart = (float) mTopPadding + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800763 Float clipEnd = clipStart + mCornerRadius;
764 boolean first = true;
765 for (int i = 0; i < getChildCount(); i++) {
766 ExpandableView child = (ExpandableView) getChildAt(i);
767 if (child.getVisibility() == GONE) {
768 continue;
769 }
770 float start = child.getTranslationY();
771 float end = start + Math.max(child.getActualHeight() - child.getClipBottomAmount(),
772 0);
773 boolean clip = clipStart > start && clipStart < end
774 || clipEnd >= start && clipEnd <= end;
775 clip &= !(first && mOwnScrollY == 0);
776 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : -1);
777 first = false;
778 }
779 }
780
Selim Cinek3776fe02016-02-04 13:32:43 -0800781 private void updateScrollStateForAddedChildren() {
782 if (mChildrenToAddAnimated.isEmpty()) {
783 return;
784 }
785 for (int i = 0; i < getChildCount(); i++) {
786 ExpandableView child = (ExpandableView) getChildAt(i);
787 if (mChildrenToAddAnimated.contains(child)) {
788 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800789 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
790 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
791 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800792 int childHeight = getIntrinsicHeight(child) + padding;
793 if (startingPosition < mOwnScrollY) {
794 // This child starts off screen, so let's keep it offscreen to keep the others visible
795
Selim Cinekef406062016-09-29 17:33:13 -0700796 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800797 }
798 }
799 }
800 clampScrollPosition();
801 }
802
Adrian Roos181385c2016-05-05 17:45:44 -0400803 private void updateForcedScroll() {
804 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
805 || !mForcedScroll.isAttachedToWindow())) {
806 mForcedScroll = null;
807 }
808 if (mForcedScroll != null) {
809 ExpandableView expandableView = (ExpandableView) mForcedScroll;
810 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
811 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700812 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400813
814 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700815
816 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
817 // that it is not visible anymore.
818 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700819 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400820 }
821 }
822 }
823
Selim Cinek319bdc42014-05-01 23:01:58 +0200824 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200825 if (!mChildrenUpdateRequested) {
826 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
827 mChildrenUpdateRequested = true;
828 invalidate();
829 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200830 }
831
Selim Cinek67b22602014-03-10 15:40:16 +0100832 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200833 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100834 }
835
Selim Cinekf7a14c02014-07-07 14:01:46 +0200836 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100837 int scrollRange = getScrollRange();
838 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700839 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100840 }
841 }
842
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200843 public int getTopPadding() {
844 return mTopPadding;
845 }
846
Selim Cinek1408eb52014-06-02 14:45:38 +0200847 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800848 if (mRegularTopPadding != topPadding) {
849 mRegularTopPadding = topPadding;
850 mDarkTopPadding = topPadding + mDarkSeparatorPadding;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800851 mAmbientState.setDarkTopPadding(mDarkTopPadding);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200852 updateAlgorithmHeightAndPadding();
853 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200854 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200855 mTopPaddingNeedsAnimation = true;
856 mNeedsAnimation = true;
857 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200858 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -0700859 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200860 }
861 }
862
863 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700864 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200865 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700866 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200867 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700868 public void setExpandedHeight(float height) {
869 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700870 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800871 int minExpansionHeight = getMinExpansionHeight();
872 if (height < minExpansionHeight) {
873 mClipRect.left = 0;
874 mClipRect.right = getWidth();
875 mClipRect.top = 0;
876 mClipRect.bottom = (int) height;
877 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700878 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800879 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700880 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800881 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200882 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700883 float translationY;
884 float appearEndPosition = getAppearEndPosition();
885 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800886 float appearFraction = 1.0f;
Selim Cinek94c2d822016-07-13 18:50:04 -0700887 if (height >= appearEndPosition) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700888 translationY = 0;
Selim Cinek94c2d822016-07-13 18:50:04 -0700889 stackHeight = (int) height;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200890 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800891 appearFraction = getAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -0700892 if (appearFraction >= 0) {
893 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
894 appearFraction);
895 } else {
896 // This may happen when pushing up a heads up. We linearly push it up from the
897 // start
898 translationY = height - appearStartPosition + getExpandTranslationStart();
899 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800900 if (isHeadsUpTransition()) {
901 stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
902 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
903 } else {
904 stackHeight = (int) (height - translationY);
905 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200906 }
907 if (stackHeight != mCurrentStackHeight) {
908 mCurrentStackHeight = stackHeight;
909 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200910 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200911 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700912 setStackTranslation(translationY);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800913 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
914 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
915 listener.accept(mExpandedHeight, appearFraction);
916 }
Selim Cinekcafa87f2016-10-26 17:00:17 -0700917 }
918
919 private void setRequestedClipBounds(Rect clipRect) {
920 mRequestedClipBounds = clipRect;
921 updateClipping();
922 }
923
Lucas Dupin60661a62018-04-12 10:50:13 -0700924 /**
925 * Return the height of the content ignoring the footer.
926 */
927 public int getIntrinsicContentHeight() {
928 return mIntrinsicContentHeight;
929 }
930
Selim Cinekcafa87f2016-10-26 17:00:17 -0700931 public void updateClipping() {
Lucas Dupin16cfe452018-02-08 13:14:50 -0800932 boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700933 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
934 && !mHeadsUpAnimatingAway;
935 if (mIsClipped != clipped) {
936 mIsClipped = clipped;
937 updateFadingState();
938 }
Lucas Dupin16cfe452018-02-08 13:14:50 -0800939
940 if (animatingClipping) {
941 setClipBounds(mBackgroundAnimationRect);
942 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700943 setClipBounds(mRequestedClipBounds);
944 } else {
945 setClipBounds(null);
946 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700947 }
948
949 /**
950 * @return The translation at the beginning when expanding.
951 * Measured relative to the resting position.
952 */
953 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800954 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700955 }
956
957 /**
958 * @return the position from where the appear transition starts when expanding.
959 * Measured in absolute height.
960 */
961 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800962 if (isHeadsUpTransition()) {
963 return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -0700964 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800965 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700966 }
967
968 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900969 * @return the height of the top heads up notification when pinned. This is different from the
970 * intrinsic height, which also includes whether the notification is system expanded and
971 * is mainly used when dragging down from a heads up notification.
972 */
973 private int getTopHeadsUpPinnedHeight() {
974 NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
975 if (topEntry == null) {
976 return 0;
977 }
978 ExpandableNotificationRow row = topEntry.row;
979 if (row.isChildInGroup()) {
980 final ExpandableNotificationRow groupSummary
981 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
982 if (groupSummary != null) {
983 row = groupSummary;
984 }
985 }
986 return row.getPinnedHeadsUpHeight();
987 }
988
989 /**
Selim Cinek94c2d822016-07-13 18:50:04 -0700990 * @return the position from where the appear transition ends when expanding.
991 * Measured in absolute height.
992 */
993 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -0700994 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -0700995 int notGoneChildCount = getNotGoneChildCount();
996 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800997 if (isHeadsUpTransition()
Selim Cinekebf42342017-07-13 15:46:10 +0200998 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900999 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +01001000 } else {
1001 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001002 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1003 appearPosition += mShelf.getIntrinsicHeight();
1004 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001005 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001006 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001007 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001008 }
1009 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001010 }
1011
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001012 private boolean isHeadsUpTransition() {
1013 return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
1014 && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
1015 }
1016
Selim Cinek94c2d822016-07-13 18:50:04 -07001017 /**
1018 * @param height the height of the panel
1019 * @return the fraction of the appear animation that has been performed
1020 */
1021 public float getAppearFraction(float height) {
1022 float appearEndPosition = getAppearEndPosition();
1023 float appearStartPosition = getAppearStartPosition();
1024 return (height - appearStartPosition)
1025 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001026 }
1027
Selim Cinekd2281152015-04-10 14:37:46 -07001028 public float getStackTranslation() {
1029 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001030 }
1031
Selim Cinekd2281152015-04-10 14:37:46 -07001032 private void setStackTranslation(float stackTranslation) {
1033 if (stackTranslation != mStackTranslation) {
1034 mStackTranslation = stackTranslation;
1035 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001036 requestChildrenUpdate();
1037 }
Selim Cinek67b22602014-03-10 15:40:16 +01001038 }
1039
1040 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001041 * 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 +01001042 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1043 *
1044 * @return either the layout height or the externally defined height, whichever is smaller
1045 */
Selim Cinek343e6e22014-04-11 21:23:30 +02001046 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001047 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1048 }
1049
Selim Cinek816c8e42015-11-19 12:00:45 -08001050 public int getFirstItemMinHeight() {
1051 final ExpandableView firstChild = getFirstChildNotGone();
1052 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001053 }
1054
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001055 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
Dan Sandler4247a5c2014-07-23 15:58:08 -04001056 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +01001057 }
1058
Jason Monk16ac3772016-02-10 15:39:21 -05001059 public void setQsContainer(ViewGroup qsContainer) {
1060 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001061 }
1062
Rohan Shah524cf7b2018-03-15 14:40:02 -07001063 /**
1064 * Handles cleanup after the given {@code view} has been fully swiped out (including
1065 * re-invoking dismiss logic in case the notification has not made its way out yet).
1066 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001067 @Override
Rohan Shah524cf7b2018-03-15 14:40:02 -07001068 public void onChildDismissed(View view) {
1069 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekd1395642016-04-28 12:22:42 -07001070 if (!row.isDismissed()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001071 handleChildViewDismissed(view);
Selim Cinekd1395642016-04-28 12:22:42 -07001072 }
1073 ViewGroup transientContainer = row.getTransientContainer();
1074 if (transientContainer != null) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001075 transientContainer.removeTransientView(view);
Selim Cinekd1395642016-04-28 12:22:42 -07001076 }
1077 }
1078
Rohan Shah524cf7b2018-03-15 14:40:02 -07001079 /**
1080 * Starts up notification dismiss and tells the notification, if any, to remove itself from
1081 * layout.
1082 *
1083 * @param view view (e.g. notification) to dismiss from the layout
1084 */
1085 private void handleChildViewDismissed(View view) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04001086 if (mDismissAllInProgress) {
1087 return;
1088 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001089
1090 boolean isBlockingHelperShown = false;
1091
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001092 setSwipingInProgress(false);
Rohan Shah524cf7b2018-03-15 14:40:02 -07001093 if (mDragAnimPendingChildren.contains(view)) {
1094 // We start the swipe and finish it in the same frame; we don't want a drag animation.
1095 mDragAnimPendingChildren.remove(view);
Selim Cinekeb973562014-05-02 17:07:49 +02001096 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001097 mAmbientState.onDragFinished(view);
Selim Cinek33223572016-02-19 19:32:22 -08001098 updateContinuousShadowDrawing();
Rohan Shah524cf7b2018-03-15 14:40:02 -07001099
1100 if (view instanceof ExpandableNotificationRow) {
1101 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001102 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -07001103 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001104 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001105 isBlockingHelperShown =
1106 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001107 }
Blazej Magnowski72323322015-07-24 11:49:40 -07001108
Rohan Shah524cf7b2018-03-15 14:40:02 -07001109 if (!isBlockingHelperShown) {
1110 mSwipedOutViews.add(view);
1111 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001112 mFalsingManager.onNotificationDismissed();
1113 if (mFalsingManager.shouldEnforceBouncer()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001114 mStatusBar.executeRunnableDismissingKeyguard(
1115 null,
1116 null /* cancelAction */,
1117 false /* dismissShade */,
1118 true /* afterKeyguardGone */,
1119 false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -07001120 }
Selim Cinekeb973562014-05-02 17:07:49 +02001121 }
1122
1123 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08001124 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001125 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -08001126 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +02001127 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001128 if (mAnimationsEnabled) {
1129 mSnappedBackChildren.add(animView);
1130 mNeedsAnimation = true;
1131 }
Selim Cinekeb973562014-05-02 17:07:49 +02001132 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +02001133 } else {
1134 // We start the swipe and snap back in the same frame, we don't want any animation
1135 mDragAnimPendingChildren.remove(animView);
1136 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001137 if (mCurrMenuRow != null && targetLeft == 0) {
1138 mCurrMenuRow.resetMenu();
1139 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08001140 }
Selim Cinek67b22602014-03-10 15:40:16 +01001141 }
1142
Adrian Roos5d9cc662014-05-28 17:08:13 +02001143 @Override
1144 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Anthony Chen7acbb772017-04-07 16:45:25 -07001145 // Returning true prevents alpha fading.
1146 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +02001147 }
1148
Mady Mellorb0a82462016-04-30 17:31:02 -07001149 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001150 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001151 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001152 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001153 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -08001154 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -07001155 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001156 mDragAnimPendingChildren.add(v);
1157 mNeedsAnimation = true;
1158 }
Selim Cinekeb973562014-05-02 17:07:49 +02001159 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +01001160 }
1161
Selim Cinek684a4422015-04-15 16:18:39 -07001162 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001163 if (v instanceof ExpandableNotificationRow) {
1164 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001165 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001166 }
1167 return false;
1168 }
1169
1170 private boolean isHeadsUp(View v) {
1171 if (v instanceof ExpandableNotificationRow) {
1172 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1173 return row.isHeadsUp();
1174 }
1175 return false;
1176 }
1177
Mady Mellorb0a82462016-04-30 17:31:02 -07001178 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001179 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001180 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001181 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001182 }
1183
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001184 @Override
1185 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001186 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001187 }
1188
Mady Mellor4b80b102016-01-22 08:03:58 -08001189 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001190 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001191 View child = getChildAtPosition(ev.getX(), ev.getY());
1192 if (child instanceof ExpandableNotificationRow) {
1193 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1194 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001195 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001196 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001197 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001198 || (parent.getNotificationChildren().size() == 1
1199 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001200 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001201 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001202 // child notifications so we use the parent of the child. We also do the same
1203 // if we only have a single child.
1204 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001205 }
1206 }
1207 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001208 }
1209
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001210 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1211 getLocationOnScreen(mTempInt2);
1212 float localTouchY = touchY - mTempInt2[1];
1213
1214 ExpandableView closestChild = null;
1215 float minDist = Float.MAX_VALUE;
1216
1217 // find the view closest to the location, accounting for GONE views
1218 final int count = getChildCount();
1219 for (int childIdx = 0; childIdx < count; childIdx++) {
1220 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1221 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001222 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001223 continue;
1224 }
1225 float childTop = slidingChild.getTranslationY();
1226 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001227 float bottom = childTop + slidingChild.getActualHeight()
1228 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001229
1230 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1231 if (dist < minDist) {
1232 closestChild = slidingChild;
1233 minDist = dist;
1234 }
1235 }
1236 return closestChild;
1237 }
1238
Mady Mellorb0a82462016-04-30 17:31:02 -07001239 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001240 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001241 getLocationOnScreen(mTempInt2);
1242 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001243 }
1244
Mady Mellorb0a82462016-04-30 17:31:02 -07001245 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001246 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001247 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1248
1249 }
1250
1251 /**
1252 * Get the child at a certain screen location.
1253 *
1254 * @param touchX the x coordinate
1255 * @param touchY the y coordinate
1256 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1257 * @return the child at the given location.
1258 */
1259 private ExpandableView getChildAtPosition(float touchX, float touchY,
1260 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001261 // find the view under the pointer, accounting for GONE views
1262 final int count = getChildCount();
1263 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001264 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001265 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001266 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001267 continue;
1268 }
Selim Cinek89faff12014-06-19 16:29:04 -07001269 float childTop = slidingChild.getTranslationY();
1270 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001271 float bottom = childTop + slidingChild.getActualHeight()
1272 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001273
1274 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1275 // camera affordance).
1276 int left = 0;
1277 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001278
Selim Cinek34ed7c02017-09-08 15:03:12 -07001279 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001280 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001281 if (slidingChild instanceof ExpandableNotificationRow) {
1282 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001283 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001284 && mHeadsUpManager.getTopEntry().row != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001285 && mGroupManager.getGroupSummary(
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001286 mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
Selim Cinek5bc852a2015-12-21 12:19:09 -08001287 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001288 continue;
1289 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001290 return row.getViewAtPosition(touchY - childTop);
1291 }
Selim Cinek67b22602014-03-10 15:40:16 +01001292 return slidingChild;
1293 }
1294 }
1295 return null;
1296 }
1297
Mady Mellorb0a82462016-04-30 17:31:02 -07001298 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001299 public boolean canChildBeExpanded(View v) {
1300 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001301 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001302 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001303 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001304 }
1305
Chris Wren6abeeb92016-05-26 14:44:38 -04001306 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001307 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001308 public void setUserExpandedChild(View v, boolean userExpanded) {
1309 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001310 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001311 if (userExpanded && onKeyguard()) {
1312 // Due to a race when locking the screen while touching, a notification may be
1313 // expanded even after we went back to keyguard. An example of this happens if
1314 // you click in the empty space while expanding a group.
1315
1316 // We also need to un-user lock it here, since otherwise the content height
1317 // calculated might be wrong. We also can't invert the two calls since
1318 // un-userlocking it will trigger a layout switch in the content view.
1319 row.setUserLocked(false);
1320 updateContentHeight();
1321 notifyHeightChangeListener(row);
1322 return;
1323 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001324 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1325 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001326 }
1327 }
1328
Mady Mellorb0a82462016-04-30 17:31:02 -07001329 @Override
1330 public void setExpansionCancelled(View v) {
1331 if (v instanceof ExpandableNotificationRow) {
1332 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1333 }
1334 }
1335
1336 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001337 public void setUserLockedChild(View v, boolean userLocked) {
1338 if (v instanceof ExpandableNotificationRow) {
1339 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1340 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001341 cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02001342 requestDisallowInterceptTouchEvent(true);
1343 }
1344
1345 @Override
1346 public void expansionStateChanged(boolean isExpanding) {
1347 mExpandingNotification = isExpanding;
1348 if (!mExpandedInThisMotion) {
1349 mMaxScrollAfterExpand = mOwnScrollY;
1350 mExpandedInThisMotion = true;
1351 }
1352 }
1353
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001354 @Override
1355 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001356 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001357 }
1358
Selim Cinek1408eb52014-06-02 14:45:38 +02001359 public void setScrollingEnabled(boolean enable) {
1360 mScrollingEnabled = enable;
1361 }
1362
Adrian Roos181385c2016-05-05 17:45:44 -04001363 public void lockScrollTo(View v) {
1364 if (mForcedScroll == v) {
1365 return;
1366 }
1367 mForcedScroll = v;
1368 scrollTo(v);
1369 }
1370
Ricky Waicd35def2016-05-03 11:07:07 +01001371 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001372 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001373 int positionInLinearLayout = getPositionInLinearLayout(v);
1374 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1375 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001376
Adrian Roos4a579672016-05-24 16:54:37 -07001377 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1378 // that it is not visible anymore.
1379 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001380 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1381 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001382 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001383 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001384 }
Ricky Waicd35def2016-05-03 11:07:07 +01001385 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001386 }
1387
Adrian Roos181385c2016-05-05 17:45:44 -04001388 /**
1389 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1390 * the IME.
1391 */
1392 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1393 return positionInLinearLayout + v.getIntrinsicHeight() +
1394 getImeInset() - getHeight() + getTopPadding();
1395 }
1396
Adrian Roos5153d4a2016-03-22 10:01:56 -07001397 @Override
1398 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001399 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001400
1401 int range = getScrollRange();
1402 if (mOwnScrollY > range) {
1403 // HACK: We're repeatedly getting staggered insets here while the IME is
1404 // animating away. To work around that we'll wait until things have settled.
1405 removeCallbacks(mReclamp);
1406 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001407 } else if (mForcedScroll != null) {
1408 // The scroll was requested before we got the actual inset - in case we need
1409 // to scroll up some more do so now.
1410 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001411 }
1412 return insets;
1413 }
1414
1415 private Runnable mReclamp = new Runnable() {
1416 @Override
1417 public void run() {
1418 int range = getScrollRange();
1419 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1420 mDontReportNextOverScroll = true;
1421 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001422 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001423 }
1424 };
1425
Selim Cinek1408eb52014-06-02 14:45:38 +02001426 public void setExpandingEnabled(boolean enable) {
1427 mExpandHelper.setEnabled(enable);
1428 }
1429
1430 private boolean isScrollingEnabled() {
1431 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001432 }
1433
Mady Mellorb0a82462016-04-30 17:31:02 -07001434 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001435 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001436 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001437 }
1438
Selim Cinek19c8c702014-08-25 22:09:19 +02001439 @Override
1440 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001441 return onKeyguard();
1442 }
1443
1444 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001445 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001446 }
1447
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001448 private void setSwipingInProgress(boolean isSwiped) {
1449 mSwipingInProgress = isSwiped;
1450 if(isSwiped) {
1451 requestDisallowInterceptTouchEvent(true);
1452 }
Selim Cinek67b22602014-03-10 15:40:16 +01001453 }
1454
1455 @Override
1456 protected void onConfigurationChanged(Configuration newConfig) {
1457 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001458 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001459 float densityScale = getResources().getDisplayMetrics().density;
1460 mSwipeHelper.setDensityScale(densityScale);
1461 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1462 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1463 initView(getContext());
1464 }
1465
Dan Sandlereceda3d2014-07-21 15:35:01 -04001466 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001467 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1468 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001469 }
1470
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001471 @Override
Mady Mellor86889c22016-04-18 16:37:06 -07001472 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001473 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001474 // If the child is showing the notification menu snap to that
1475 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001476 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001477 }
1478
Selim Cinek67b22602014-03-10 15:40:16 +01001479 @Override
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001480 public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
1481 return this;
1482 }
1483
1484 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001485 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001486 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1487 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001488 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001489 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001490 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001491 if (isCancelOrUp) {
1492 mExpandHelper.onlyObserveMovements(false);
1493 }
1494 boolean wasExpandingBefore = mExpandingNotification;
1495 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001496 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1497 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001498 dispatchDownEventToScroller(ev);
1499 }
1500 }
Selim Cinek67b22602014-03-10 15:40:16 +01001501 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001502 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1503 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001504 scrollerWantsIt = onScrollTouch(ev);
1505 }
1506 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001507 if (!mIsBeingDragged
1508 && !mExpandingNotification
1509 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001510 && !mOnlyScrollingInThisMotion
1511 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001512 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1513 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001514
1515 // Check if we need to clear any snooze leavebehinds
Eliot Courtney47098cb2017-10-18 17:30:30 +09001516 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07001517 if (guts != null && !isTouchInView(ev, guts)
1518 && guts.getGutsContent() instanceof NotificationSnooze) {
1519 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1520 if ((ns.isExpanded() && isCancelOrUp)
1521 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1522 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1523 // clear it on the next non-horizontal swipe or expand event.
1524 checkSnoozeLeavebehind();
1525 }
1526 }
1527 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1528 mCheckForLeavebehind = true;
1529 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001530 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1531 }
1532
1533 private void dispatchDownEventToScroller(MotionEvent ev) {
1534 MotionEvent downEvent = MotionEvent.obtain(ev);
1535 downEvent.setAction(MotionEvent.ACTION_DOWN);
1536 onScrollTouch(downEvent);
1537 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001538 }
1539
Selim Cinek11e33232016-08-05 15:30:53 -07001540 @Override
1541 public boolean onGenericMotionEvent(MotionEvent event) {
1542 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1543 || mDisallowScrollingInThisMotion) {
1544 return false;
1545 }
1546 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1547 switch (event.getAction()) {
1548 case MotionEvent.ACTION_SCROLL: {
1549 if (!mIsBeingDragged) {
1550 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1551 if (vscroll != 0) {
1552 final int delta = (int) (vscroll * getVerticalScrollFactor());
1553 final int range = getScrollRange();
1554 int oldScrollY = mOwnScrollY;
1555 int newScrollY = oldScrollY - delta;
1556 if (newScrollY < 0) {
1557 newScrollY = 0;
1558 } else if (newScrollY > range) {
1559 newScrollY = range;
1560 }
1561 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001562 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001563 return true;
1564 }
1565 }
1566 }
1567 }
1568 }
1569 }
1570 return super.onGenericMotionEvent(event);
1571 }
1572
Selim Cinek67b22602014-03-10 15:40:16 +01001573 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001574 if (!isScrollingEnabled()) {
1575 return false;
1576 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001577 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001578 return false;
1579 }
Adrian Roos181385c2016-05-05 17:45:44 -04001580 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001581 initVelocityTrackerIfNotExists();
1582 mVelocityTracker.addMovement(ev);
1583
1584 final int action = ev.getAction();
1585
1586 switch (action & MotionEvent.ACTION_MASK) {
1587 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001588 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001589 return false;
1590 }
1591 boolean isBeingDragged = !mScroller.isFinished();
1592 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001593 /*
1594 * If being flinged and user touches, stop the fling. isFinished
1595 * will be false if being flinged.
1596 */
1597 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001598 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001599 }
1600
1601 // Remember where the motion event started
1602 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001603 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001604 mActivePointerId = ev.getPointerId(0);
1605 break;
1606 }
1607 case MotionEvent.ACTION_MOVE:
1608 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1609 if (activePointerIndex == -1) {
1610 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1611 break;
1612 }
1613
1614 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001615 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001616 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001617 final int xDiff = Math.abs(x - mDownX);
1618 final int yDiff = Math.abs(deltaY);
1619 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001620 setIsBeingDragged(true);
1621 if (deltaY > 0) {
1622 deltaY -= mTouchSlop;
1623 } else {
1624 deltaY += mTouchSlop;
1625 }
1626 }
1627 if (mIsBeingDragged) {
1628 // Scroll to follow the motion event
1629 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001630 int range = getScrollRange();
1631 if (mExpandedInThisMotion) {
1632 range = Math.min(range, mMaxScrollAfterExpand);
1633 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001634
1635 float scrollAmount;
1636 if (deltaY < 0) {
1637 scrollAmount = overScrollDown(deltaY);
1638 } else {
1639 scrollAmount = overScrollUp(deltaY, range);
1640 }
Selim Cinek67b22602014-03-10 15:40:16 +01001641
Selim Cinek9212de82017-02-06 16:04:28 -08001642 // Calling customOverScrollBy will call onCustomOverScrolled, which
1643 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001644 if (scrollAmount != 0.0f) {
1645 // The scrolling motion could not be compensated with the
1646 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001647 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1648 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001649 // If we're scrolling, leavebehinds should be dismissed
1650 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001651 }
Selim Cinek67b22602014-03-10 15:40:16 +01001652 }
1653 break;
1654 case MotionEvent.ACTION_UP:
1655 if (mIsBeingDragged) {
1656 final VelocityTracker velocityTracker = mVelocityTracker;
1657 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1658 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1659
Selim Cinek1408eb52014-06-02 14:45:38 +02001660 if (shouldOverScrollFling(initialVelocity)) {
1661 onOverScrollFling(true, initialVelocity);
1662 } else {
1663 if (getChildCount() > 0) {
1664 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1665 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1666 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1667 fling(-initialVelocity);
1668 } else {
1669 onOverScrollFling(false, initialVelocity);
1670 }
1671 } else {
1672 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1673 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001674 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001675 }
Selim Cinek67b22602014-03-10 15:40:16 +01001676 }
1677 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001678 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001679 mActivePointerId = INVALID_POINTER;
1680 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001681 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001682
Selim Cinek67b22602014-03-10 15:40:16 +01001683 break;
1684 case MotionEvent.ACTION_CANCEL:
1685 if (mIsBeingDragged && getChildCount() > 0) {
1686 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001687 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001688 }
1689 mActivePointerId = INVALID_POINTER;
1690 endDrag();
1691 }
1692 break;
1693 case MotionEvent.ACTION_POINTER_DOWN: {
1694 final int index = ev.getActionIndex();
1695 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001696 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001697 mActivePointerId = ev.getPointerId(index);
1698 break;
1699 }
1700 case MotionEvent.ACTION_POINTER_UP:
1701 onSecondaryPointerUp(ev);
1702 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001703 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001704 break;
1705 }
1706 return true;
1707 }
1708
Muyuan Li84b45612016-06-01 11:05:08 -07001709 protected boolean isInsideQsContainer(MotionEvent ev) {
1710 return ev.getY() < mQsContainer.getBottom();
1711 }
1712
Selim Cinek1408eb52014-06-02 14:45:38 +02001713 private void onOverScrollFling(boolean open, int initialVelocity) {
1714 if (mOverscrollTopChangedListener != null) {
1715 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1716 }
1717 mDontReportNextOverScroll = true;
1718 setOverScrollAmount(0.0f, true, false);
1719 }
1720
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001721 /**
1722 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1723 *
1724 * @param deltaY The amount to scroll upwards, has to be positive.
1725 * @return The amount of scrolling to be performed by the scroller,
1726 * not handled by the overScroll amount.
1727 */
1728 private float overScrollUp(int deltaY, int range) {
1729 deltaY = Math.max(deltaY, 0);
1730 float currentTopAmount = getCurrentOverScrollAmount(true);
1731 float newTopAmount = currentTopAmount - deltaY;
1732 if (currentTopAmount > 0) {
1733 setOverScrollAmount(newTopAmount, true /* onTop */,
1734 false /* animate */);
1735 }
1736 // Top overScroll might not grab all scrolling motion,
1737 // we have to scroll as well.
1738 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1739 float newScrollY = mOwnScrollY + scrollAmount;
1740 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001741 if (!mExpandedInThisMotion) {
1742 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1743 // We overScroll on the top
1744 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1745 false /* onTop */,
1746 false /* animate */);
1747 }
Selim Cinekef406062016-09-29 17:33:13 -07001748 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001749 scrollAmount = 0.0f;
1750 }
1751 return scrollAmount;
1752 }
1753
1754 /**
1755 * Perform a scroll downward and adapt the overscroll amounts accordingly
1756 *
1757 * @param deltaY The amount to scroll downwards, has to be negative.
1758 * @return The amount of scrolling to be performed by the scroller,
1759 * not handled by the overScroll amount.
1760 */
1761 private float overScrollDown(int deltaY) {
1762 deltaY = Math.min(deltaY, 0);
1763 float currentBottomAmount = getCurrentOverScrollAmount(false);
1764 float newBottomAmount = currentBottomAmount + deltaY;
1765 if (currentBottomAmount > 0) {
1766 setOverScrollAmount(newBottomAmount, false /* onTop */,
1767 false /* animate */);
1768 }
1769 // Bottom overScroll might not grab all scrolling motion,
1770 // we have to scroll as well.
1771 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1772 float newScrollY = mOwnScrollY + scrollAmount;
1773 if (newScrollY < 0) {
1774 float currentTopPixels = getCurrentOverScrolledPixels(true);
1775 // We overScroll on the top
1776 setOverScrolledPixels(currentTopPixels - newScrollY,
1777 true /* onTop */,
1778 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001779 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001780 scrollAmount = 0.0f;
1781 }
1782 return scrollAmount;
1783 }
1784
Selim Cinek67b22602014-03-10 15:40:16 +01001785 private void onSecondaryPointerUp(MotionEvent ev) {
1786 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1787 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1788 final int pointerId = ev.getPointerId(pointerIndex);
1789 if (pointerId == mActivePointerId) {
1790 // This was our active pointer going up. Choose a new
1791 // active pointer and adjust accordingly.
1792 // TODO: Make this decision more intelligent.
1793 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1794 mLastMotionY = (int) ev.getY(newPointerIndex);
1795 mActivePointerId = ev.getPointerId(newPointerIndex);
1796 if (mVelocityTracker != null) {
1797 mVelocityTracker.clear();
1798 }
1799 }
1800 }
1801
1802 private void initVelocityTrackerIfNotExists() {
1803 if (mVelocityTracker == null) {
1804 mVelocityTracker = VelocityTracker.obtain();
1805 }
1806 }
1807
1808 private void recycleVelocityTracker() {
1809 if (mVelocityTracker != null) {
1810 mVelocityTracker.recycle();
1811 mVelocityTracker = null;
1812 }
1813 }
1814
1815 private void initOrResetVelocityTracker() {
1816 if (mVelocityTracker == null) {
1817 mVelocityTracker = VelocityTracker.obtain();
1818 } else {
1819 mVelocityTracker.clear();
1820 }
1821 }
1822
Ricky Waicd35def2016-05-03 11:07:07 +01001823 public void setFinishScrollingCallback(Runnable runnable) {
1824 mFinishScrollingCallback = runnable;
1825 }
1826
Selim Cinek9212de82017-02-06 16:04:28 -08001827 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001828 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001829 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001830 int y = mScroller.getCurrY();
1831
Selim Cinek9212de82017-02-06 16:04:28 -08001832 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001833 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001834 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1835 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001836 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001837 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1838 }
1839 }
Selim Cinek67b22602014-03-10 15:40:16 +01001840
Adrian Roos5153d4a2016-03-22 10:01:56 -07001841 if (mDontClampNextScroll) {
1842 range = Math.max(range, oldY);
1843 }
Selim Cinek9212de82017-02-06 16:04:28 -08001844 customOverScrollBy(y - oldY, oldY, range,
1845 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001846 }
1847
Selim Cinek9212de82017-02-06 16:04:28 -08001848 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001849 } else {
1850 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001851 if (mFinishScrollingCallback != null) {
1852 mFinishScrollingCallback.run();
1853 }
Selim Cinek67b22602014-03-10 15:40:16 +01001854 }
1855 }
1856
Selim Cinek9212de82017-02-06 16:04:28 -08001857 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1858 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001859
1860 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001861 final int top = -maxOverScrollY;
1862 final int bottom = maxOverScrollY + scrollRangeY;
1863
1864 boolean clampedY = false;
1865 if (newScrollY > bottom) {
1866 newScrollY = bottom;
1867 clampedY = true;
1868 } else if (newScrollY < top) {
1869 newScrollY = top;
1870 clampedY = true;
1871 }
1872
Selim Cinek9212de82017-02-06 16:04:28 -08001873 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001874
1875 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001876 }
1877
1878 /**
1879 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1880 * overscroll effect based on numPixels. By default this will also cancel animations on the
1881 * same overScroll edge.
1882 *
1883 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1884 * the rubber-banding logic.
1885 * @param onTop Should the effect be applied on top of the scroller.
1886 * @param animate Should an animation be performed.
1887 */
1888 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001889 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001890 }
1891
1892 /**
1893 * Set the effective overScroll amount which will be directly reflected in the layout.
1894 * By default this will also cancel animations on the same overScroll edge.
1895 *
1896 * @param amount The amount to overScroll by.
1897 * @param onTop Should the effect be applied on top of the scroller.
1898 * @param animate Should an animation be performed.
1899 */
1900 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1901 setOverScrollAmount(amount, onTop, animate, true);
1902 }
1903
1904 /**
1905 * Set the effective overScroll amount which will be directly reflected in the layout.
1906 *
1907 * @param amount The amount to overScroll by.
1908 * @param onTop Should the effect be applied on top of the scroller.
1909 * @param animate Should an animation be performed.
1910 * @param cancelAnimators Should running animations be cancelled.
1911 */
1912 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1913 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001914 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1915 }
1916
1917 /**
1918 * Set the effective overScroll amount which will be directly reflected in the layout.
1919 *
1920 * @param amount The amount to overScroll by.
1921 * @param onTop Should the effect be applied on top of the scroller.
1922 * @param animate Should an animation be performed.
1923 * @param cancelAnimators Should running animations be cancelled.
1924 * @param isRubberbanded The value which will be passed to
1925 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1926 */
1927 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1928 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001929 if (cancelAnimators) {
1930 mStateAnimator.cancelOverScrollAnimators(onTop);
1931 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001932 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001933 }
1934
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001935 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1936 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001937 amount = Math.max(0, amount);
1938 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001939 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001940 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001941 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001942 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001943 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001944 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001945 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001946 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001947 }
1948 }
1949
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001950 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001951 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1952 if (mDontReportNextOverScroll) {
1953 mDontReportNextOverScroll = false;
1954 return;
1955 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001956 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001957 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001958 }
1959 }
1960
1961 public void setOverscrollTopChangedListener(
1962 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1963 mOverscrollTopChangedListener = overscrollTopChangedListener;
1964 }
1965
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001966 public float getCurrentOverScrollAmount(boolean top) {
1967 return mAmbientState.getOverScrollAmount(top);
1968 }
1969
1970 public float getCurrentOverScrolledPixels(boolean top) {
1971 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
1972 }
1973
1974 private void setOverScrolledPixels(float amount, boolean onTop) {
1975 if (onTop) {
1976 mOverScrolledTopPixels = amount;
1977 } else {
1978 mOverScrolledBottomPixels = amount;
1979 }
1980 }
1981
Selim Cinek9212de82017-02-06 16:04:28 -08001982 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001983 // Treat animating scrolls differently; see #computeScroll() for why.
1984 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07001985 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001986 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001987 springBack();
1988 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02001989 float overScrollTop = getCurrentOverScrollAmount(true);
1990 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001991 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001992 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001993 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001994 }
Selim Cinek67b22602014-03-10 15:40:16 +01001995 }
Selim Cinek67b22602014-03-10 15:40:16 +01001996 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08001997 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001998 }
1999 }
2000
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002001 private void springBack() {
2002 int scrollRange = getScrollRange();
2003 boolean overScrolledTop = mOwnScrollY <= 0;
2004 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2005 if (overScrolledTop || overScrolledBottom) {
2006 boolean onTop;
2007 float newAmount;
2008 if (overScrolledTop) {
2009 onTop = true;
2010 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07002011 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02002012 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002013 } else {
2014 onTop = false;
2015 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07002016 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002017 }
2018 setOverScrollAmount(newAmount, onTop, false);
2019 setOverScrollAmount(0.0f, onTop, true);
2020 mScroller.forceFinished(true);
2021 }
2022 }
2023
Selim Cinek67b22602014-03-10 15:40:16 +01002024 private int getScrollRange() {
Selim Cineka424c502016-04-05 13:07:54 -07002025 int contentHeight = getContentHeight();
Selim Cinekdb167372016-11-17 15:41:17 -08002026 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002027 int imeInset = getImeInset();
2028 scrollRange += Math.min(imeInset, Math.max(0,
2029 getContentHeight() - (getHeight() - imeInset)));
2030 return scrollRange;
2031 }
2032
2033 private int getImeInset() {
2034 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002035 }
2036
Selim Cinek343e6e22014-04-11 21:23:30 +02002037 /**
2038 * @return the first child which has visibility unequal to GONE
2039 */
Selim Cinekb55386d2015-12-16 17:26:49 -08002040 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002041 int childCount = getChildCount();
2042 for (int i = 0; i < childCount; i++) {
2043 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002044 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002045 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002046 }
2047 }
2048 return null;
2049 }
2050
Selim Cinek4a1ac842014-05-01 15:51:58 +02002051 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002052 * @return the child before the given view which has visibility unequal to GONE
2053 */
2054 public ExpandableView getViewBeforeView(ExpandableView view) {
2055 ExpandableView previousView = null;
2056 int childCount = getChildCount();
2057 for (int i = 0; i < childCount; i++) {
2058 View child = getChildAt(i);
2059 if (child == view) {
2060 return previousView;
2061 }
2062 if (child.getVisibility() != View.GONE) {
2063 previousView = (ExpandableView) child;
2064 }
2065 }
2066 return null;
2067 }
2068
2069 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002070 * @return The first child which has visibility unequal to GONE which is currently below the
2071 * given translationY or equal to it.
2072 */
Selim Cinekef8c2252017-02-10 14:52:18 -08002073 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002074 int childCount = getChildCount();
2075 for (int i = 0; i < childCount; i++) {
2076 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002077 if (child.getVisibility() == View.GONE) {
2078 continue;
2079 }
2080 float rowTranslation = child.getTranslationY();
2081 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002082 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002083 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2084 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2085 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2086 List<ExpandableNotificationRow> notificationChildren =
2087 row.getNotificationChildren();
2088 for (int childIndex = 0; childIndex < notificationChildren.size();
2089 childIndex++) {
2090 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2091 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2092 return rowChild;
2093 }
2094 }
2095 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002096 }
2097 }
2098 return null;
2099 }
2100
2101 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002102 * @return the last child which has visibility unequal to GONE
2103 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002104 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002105 int childCount = getChildCount();
2106 for (int i = childCount - 1; i >= 0; i--) {
2107 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002108 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002109 return child;
2110 }
2111 }
2112 return null;
2113 }
2114
Jorim Jaggi069cd032014-05-15 03:09:01 +02002115 /**
2116 * @return the number of children which have visibility unequal to GONE
2117 */
2118 public int getNotGoneChildCount() {
2119 int childCount = getChildCount();
2120 int count = 0;
2121 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002122 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002123 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002124 count++;
2125 }
2126 }
2127 return count;
2128 }
2129
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002130 public int getContentHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01002131 return mContentHeight;
2132 }
2133
2134 private void updateContentHeight() {
2135 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002136 float previousPaddingRequest = mPaddingBetweenElements;
2137 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002138 int numShownItems = 0;
2139 boolean finish = false;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002140 int maxDisplayedNotifications = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002141 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08002142 : mMaxDisplayedNotifications;
2143
Selim Cinek67b22602014-03-10 15:40:16 +01002144 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002145 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002146 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002147 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002148 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002149 boolean limitReached = maxDisplayedNotifications != -1
2150 && numShownItems >= maxDisplayedNotifications;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002151 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002152 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07002153 && expandableView instanceof ExpandableNotificationRow
2154 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
2155 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002156 expandableView = mShelf;
2157 finish = true;
2158 }
Selim Cinek42357e02016-02-24 18:48:01 -08002159 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002160 float padding;
2161 if (increasedPaddingAmount >= 0.0f) {
2162 padding = (int) NotificationUtils.interpolate(
2163 previousPaddingRequest,
2164 mIncreasedPaddingBetweenElements,
2165 increasedPaddingAmount);
2166 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002167 mPaddingBetweenElements,
2168 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002169 increasedPaddingAmount);
2170 } else {
2171 int ownPadding = (int) NotificationUtils.interpolate(
2172 0,
2173 mPaddingBetweenElements,
2174 1.0f + increasedPaddingAmount);
2175 if (previousPaddingAmount > 0.0f) {
2176 padding = (int) NotificationUtils.interpolate(
2177 ownPadding,
2178 mIncreasedPaddingBetweenElements,
2179 previousPaddingAmount);
2180 } else {
2181 padding = ownPadding;
2182 }
2183 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002184 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002185 if (height != 0) {
2186 height += padding;
2187 }
2188 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002189 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002190 numShownItems++;
2191 if (finish) {
2192 break;
2193 }
Selim Cinek67b22602014-03-10 15:40:16 +01002194 }
2195 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002196 mIntrinsicContentHeight = height;
Anthony Chen9fe1ee72017-04-07 13:53:37 -07002197 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002198 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002199 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002200 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002201 }
2202
Adrian Roos7d062c42017-03-30 15:11:43 -07002203 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002204 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002205 }
2206
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002207 @Override
Selim Cinekbe2c4432017-05-30 12:11:09 -07002208 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002209 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002210 }
2211
Selim Cinekc22fff62016-05-20 12:44:30 -07002212 private void updateScrollability() {
2213 boolean scrollable = getScrollRange() > 0;
2214 if (scrollable != mScrollable) {
2215 mScrollable = scrollable;
2216 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002217 updateForwardAndBackwardScrollability();
2218 }
2219 }
2220
2221 private void updateForwardAndBackwardScrollability() {
2222 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2223 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2224 boolean changed = forwardScrollable != mForwardScrollable
2225 || backwardsScrollable != mBackwardScrollable;
2226 mForwardScrollable = forwardScrollable;
2227 mBackwardScrollable = backwardsScrollable;
2228 if (changed) {
2229 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002230 }
Selim Cinek67b22602014-03-10 15:40:16 +01002231 }
2232
Selim Cinek6811d722016-01-19 17:53:12 -08002233 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002234 // No need to update the background color if it's not being drawn.
Lucas Dupin16cfe452018-02-08 13:14:50 -08002235 if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002236 return;
2237 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002238
Selim Cinek6811d722016-01-19 17:53:12 -08002239 updateBackgroundBounds();
2240 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002241 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2242 || areBoundsAnimating();
2243 if (!isExpanded()) {
2244 abortBackgroundAnimators();
2245 animate = false;
2246 }
2247 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002248 startBackgroundAnimation();
2249 } else {
2250 mCurrentBounds.set(mBackgroundBounds);
2251 applyCurrentBackgroundBounds();
2252 }
2253 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002254 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002255 }
Selim Cinek614576e2016-01-20 10:54:09 -08002256 mAnimateNextBackgroundBottom = false;
2257 mAnimateNextBackgroundTop = false;
2258 }
2259
Selim Cinek54680902016-10-19 16:49:44 -07002260 private void abortBackgroundAnimators() {
2261 if (mBottomAnimator != null) {
2262 mBottomAnimator.cancel();
2263 }
2264 if (mTopAnimator != null) {
2265 mTopAnimator.cancel();
2266 }
2267 }
2268
Selim Cinek614576e2016-01-20 10:54:09 -08002269 private boolean areBoundsAnimating() {
2270 return mBottomAnimator != null || mTopAnimator != null;
2271 }
2272
2273 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002274 // left and right are always instantly applied
2275 mCurrentBounds.left = mBackgroundBounds.left;
2276 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002277 startBottomAnimation();
2278 startTopAnimation();
2279 }
2280
2281 private void startTopAnimation() {
2282 int previousEndValue = mEndAnimationRect.top;
2283 int newEndValue = mBackgroundBounds.top;
2284 ObjectAnimator previousAnimator = mTopAnimator;
2285 if (previousAnimator != null && previousEndValue == newEndValue) {
2286 return;
2287 }
2288 if (!mAnimateNextBackgroundTop) {
2289 // just a local update was performed
2290 if (previousAnimator != null) {
2291 // we need to increase all animation keyframes of the previous animator by the
2292 // relative change to the end value
2293 int previousStartValue = mStartAnimationRect.top;
2294 PropertyValuesHolder[] values = previousAnimator.getValues();
2295 values[0].setIntValues(previousStartValue, newEndValue);
2296 mStartAnimationRect.top = previousStartValue;
2297 mEndAnimationRect.top = newEndValue;
2298 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2299 return;
2300 } else {
2301 // no new animation needed, let's just apply the value
2302 setBackgroundTop(newEndValue);
2303 return;
2304 }
2305 }
2306 if (previousAnimator != null) {
2307 previousAnimator.cancel();
2308 }
2309 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2310 mCurrentBounds.top, newEndValue);
2311 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2312 animator.setInterpolator(interpolator);
2313 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2314 // remove the tag when the animation is finished
2315 animator.addListener(new AnimatorListenerAdapter() {
2316 @Override
2317 public void onAnimationEnd(Animator animation) {
2318 mStartAnimationRect.top = -1;
2319 mEndAnimationRect.top = -1;
2320 mTopAnimator = null;
2321 }
2322 });
2323 animator.start();
2324 mStartAnimationRect.top = mCurrentBounds.top;
2325 mEndAnimationRect.top = newEndValue;
2326 mTopAnimator = animator;
2327 }
2328
2329 private void startBottomAnimation() {
2330 int previousStartValue = mStartAnimationRect.bottom;
2331 int previousEndValue = mEndAnimationRect.bottom;
2332 int newEndValue = mBackgroundBounds.bottom;
2333 ObjectAnimator previousAnimator = mBottomAnimator;
2334 if (previousAnimator != null && previousEndValue == newEndValue) {
2335 return;
2336 }
2337 if (!mAnimateNextBackgroundBottom) {
2338 // just a local update was performed
2339 if (previousAnimator != null) {
2340 // we need to increase all animation keyframes of the previous animator by the
2341 // relative change to the end value
2342 PropertyValuesHolder[] values = previousAnimator.getValues();
2343 values[0].setIntValues(previousStartValue, newEndValue);
2344 mStartAnimationRect.bottom = previousStartValue;
2345 mEndAnimationRect.bottom = newEndValue;
2346 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2347 return;
2348 } else {
2349 // no new animation needed, let's just apply the value
2350 setBackgroundBottom(newEndValue);
2351 return;
2352 }
2353 }
2354 if (previousAnimator != null) {
2355 previousAnimator.cancel();
2356 }
2357 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2358 mCurrentBounds.bottom, newEndValue);
2359 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2360 animator.setInterpolator(interpolator);
2361 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2362 // remove the tag when the animation is finished
2363 animator.addListener(new AnimatorListenerAdapter() {
2364 @Override
2365 public void onAnimationEnd(Animator animation) {
2366 mStartAnimationRect.bottom = -1;
2367 mEndAnimationRect.bottom = -1;
2368 mBottomAnimator = null;
2369 }
2370 });
2371 animator.start();
2372 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2373 mEndAnimationRect.bottom = newEndValue;
2374 mBottomAnimator = animator;
2375 }
2376
2377 private void setBackgroundTop(int top) {
2378 mCurrentBounds.top = top;
2379 applyCurrentBackgroundBounds();
2380 }
2381
2382 public void setBackgroundBottom(int bottom) {
2383 mCurrentBounds.bottom = bottom;
2384 applyCurrentBackgroundBounds();
2385 }
2386
2387 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002388 // If the background of the notification is not being drawn, then there is no need to
2389 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2390 if (!mShouldDrawNotificationBackground) {
2391 return;
2392 }
2393
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002394 final boolean awake = mDarkAmount != 0 || mAmbientState.isDark();
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002395 mScrimController.setExcludedBackgroundArea(
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002396 mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002397 : mCurrentBounds);
Selim Cinek614576e2016-01-20 10:54:09 -08002398 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002399 }
2400
2401 /**
2402 * Update the background bounds to the new desired bounds
2403 */
2404 private void updateBackgroundBounds() {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01002405 if (mAmbientState.isPanelFullWidth()) {
2406 mBackgroundBounds.left = 0;
2407 mBackgroundBounds.right = getWidth();
2408 } else {
2409 getLocationInWindow(mTempInt2);
2410 mBackgroundBounds.left = mTempInt2[0];
2411 mBackgroundBounds.right = mTempInt2[0] + getWidth();
2412 }
Selim Cinekb95fd182017-12-21 13:03:32 -08002413 mBackgroundBounds.left += mSidePaddings;
2414 mBackgroundBounds.right -= mSidePaddings;
Selim Cinek614576e2016-01-20 10:54:09 -08002415 if (!mIsExpanded) {
2416 mBackgroundBounds.top = 0;
2417 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002418 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002419 }
2420 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002421 int top = 0;
2422 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002423 // Round Y up to avoid seeing the background during animation
2424 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002425 if (mAnimateNextBackgroundTop
2426 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2427 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002428 // we're ending up at the same location as we are now, lets just skip the animation
2429 top = finalTranslationY;
2430 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002431 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002432 }
2433 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002434 ActivatableNotificationView lastView =
2435 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2436 ? mShelf
2437 : mLastVisibleBackgroundChild;
2438 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002439 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002440 int finalTranslationY;
2441 if (lastView == mShelf) {
2442 finalTranslationY = (int) mShelf.getTranslationY();
2443 } else {
2444 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2445 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002446 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002447 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek6811d722016-01-19 17:53:12 -08002448 finalBottom = Math.min(finalBottom, getHeight());
Selim Cinek614576e2016-01-20 10:54:09 -08002449 if (mAnimateNextBackgroundBottom
2450 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2451 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002452 // we're ending up at the same location as we are now, lets just skip the animation
2453 bottom = finalBottom;
2454 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002455 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2456 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002457 bottom = Math.min(bottom, getHeight());
2458 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002459 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002460 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002461 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002462 }
Selim Cinek355652a2016-12-07 13:32:12 -08002463 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002464 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002465 } else {
2466 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002467 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002468 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002469 mBackgroundBounds.top = top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002470 mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
Selim Cinek6811d722016-01-19 17:53:12 -08002471 }
2472
Selim Cinek614576e2016-01-20 10:54:09 -08002473 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2474 int childCount = getChildCount();
2475 for (int i = 0; i < childCount; i++) {
2476 View child = getChildAt(i);
2477 if (child.getVisibility() != View.GONE
2478 && child instanceof ExpandableNotificationRow) {
2479 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2480 if (row.isPinned()) {
2481 return row;
2482 }
2483 }
2484 }
2485 return null;
2486 }
2487
2488 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002489 int childCount = getChildCount();
2490 for (int i = childCount - 1; i >= 0; i--) {
2491 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002492 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2493 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002494 return (ActivatableNotificationView) child;
2495 }
2496 }
2497 return null;
2498 }
2499
Selim Cinek614576e2016-01-20 10:54:09 -08002500 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002501 int childCount = getChildCount();
2502 for (int i = 0; i < childCount; i++) {
2503 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002504 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2505 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002506 return (ActivatableNotificationView) child;
2507 }
2508 }
2509 return null;
2510 }
2511
Selim Cinek67b22602014-03-10 15:40:16 +01002512 /**
2513 * Fling the scroll view
2514 *
2515 * @param velocityY The initial velocity in the Y direction. Positive
2516 * numbers mean that the finger/cursor is moving down the screen,
2517 * which means we want to scroll towards the top.
2518 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002519 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002520 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002521 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002522
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002523 float topAmount = getCurrentOverScrollAmount(true);
2524 float bottomAmount = getCurrentOverScrollAmount(false);
2525 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002526 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002527 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002528 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002529 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002530 * mOverflingDistance + topAmount;
2531 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002532 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002533 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002534 mMaxOverScroll = Math.abs(velocityY) / 1000f
2535 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2536 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002537 } else {
2538 // it will be set once we reach the boundary
2539 mMaxOverScroll = 0.0f;
2540 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002541 int minScrollY = Math.max(0, scrollRange);
2542 if (mExpandedInThisMotion) {
2543 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2544 }
Selim Cinek9212de82017-02-06 16:04:28 -08002545 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2546 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002547
Selim Cinek9212de82017-02-06 16:04:28 -08002548 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002549 }
2550 }
2551
Selim Cinek1408eb52014-06-02 14:45:38 +02002552 /**
2553 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2554 * overScroll view (i.e QS).
2555 */
2556 private boolean shouldOverScrollFling(int initialVelocity) {
2557 float topOverScroll = getCurrentOverScrollAmount(true);
2558 return mScrolledToTopOnFirstDown
2559 && !mExpandedInThisMotion
2560 && topOverScroll > mMinTopOverScrollToEscape
2561 && initialVelocity > 0;
2562 }
2563
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002564 /**
2565 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2566 * account.
2567 *
2568 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002569 * @param animate whether to animate the change
2570 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2571 * {@code qsHeight} is the final top padding
2572 */
Jason Monk16ac3772016-02-10 15:39:21 -05002573 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002574 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002575 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002576 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002577 if (topPadding + minStackHeight > getHeight()) {
2578 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002579 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002580 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002581 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002582 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002583 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002584 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002585 }
2586
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002587 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002588 if (isHeadsUpTransition()) {
2589 return getTopHeadsUpPinnedHeight();
2590 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002591 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002592 }
2593
Selim Cineka981d082016-08-03 14:45:31 -07002594 public int getFirstChildIntrinsicHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002595 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002596 int firstChildMinHeight = firstChild != null
2597 ? firstChild.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002598 : mEmptyShadeView != null
Selim Cineka981d082016-08-03 14:45:31 -07002599 ? mEmptyShadeView.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002600 : mCollapsedSize;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002601 if (mOwnScrollY > 0) {
2602 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2603 }
Selim Cinek94c2d822016-07-13 18:50:04 -07002604 return firstChildMinHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002605 }
2606
2607 public float getTopPaddingOverflow() {
2608 return mTopPaddingOverflow;
2609 }
2610
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002611 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002612 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002613 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002614 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002615 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002616 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002617 shelfHeight = mShelf.getIntrinsicHeight();
2618 }
2619 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002620 }
2621
Selim Cinek1408eb52014-06-02 14:45:38 +02002622 private int clampPadding(int desiredPadding) {
2623 return Math.max(desiredPadding, mIntrinsicPadding);
2624 }
2625
Selim Cinekfed1ab62014-06-17 14:10:33 -07002626 private float getRubberBandFactor(boolean onTop) {
2627 if (!onTop) {
2628 return RUBBER_BAND_FACTOR_NORMAL;
2629 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002630 if (mExpandedInThisMotion) {
2631 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002632 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002633 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2634 } else if (mScrolledToTopOnFirstDown) {
2635 return 1.0f;
2636 }
2637 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002638 }
2639
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002640 /**
2641 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2642 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2643 * overscroll view (e.g. expand QS).
2644 */
2645 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002646 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002647 || !mScrolledToTopOnFirstDown;
2648 }
2649
Selim Cinek67b22602014-03-10 15:40:16 +01002650 private void endDrag() {
2651 setIsBeingDragged(false);
2652
2653 recycleVelocityTracker();
2654
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002655 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2656 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2657 }
2658 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2659 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2660 }
Selim Cinek67b22602014-03-10 15:40:16 +01002661 }
2662
Jorim Jaggi56306252014-07-03 00:40:09 +02002663 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2664 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2665 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2666 }
2667
Selim Cinek67b22602014-03-10 15:40:16 +01002668 @Override
2669 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002670 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002671 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002672 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002673 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002674 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2675 }
Selim Cinek67b22602014-03-10 15:40:16 +01002676 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002677 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002678 scrollWantsIt = onInterceptTouchEventScroll(ev);
2679 }
2680 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002681 if (!mIsBeingDragged
2682 && !mExpandingNotification
2683 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002684 && !mOnlyScrollingInThisMotion
2685 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002686 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2687 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002688 // Check if we need to clear any snooze leavebehinds
2689 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002690 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07002691 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2692 && !scrollWantsIt) {
2693 mCheckForLeavebehind = false;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002694 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
2695 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2696 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07002697 }
2698 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2699 mCheckForLeavebehind = true;
2700 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002701 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2702 }
2703
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002704 private void handleEmptySpaceClick(MotionEvent ev) {
2705 switch (ev.getActionMasked()) {
2706 case MotionEvent.ACTION_MOVE:
2707 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2708 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2709 mTouchIsClick = false;
2710 }
2711 break;
2712 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002713 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002714 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2715 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2716 }
2717 break;
2718 }
2719 }
2720
Selim Cinek1408eb52014-06-02 14:45:38 +02002721 private void initDownStates(MotionEvent ev) {
2722 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2723 mExpandedInThisMotion = false;
2724 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002725 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002726 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002727 mTouchIsClick = true;
2728 mInitialTouchX = ev.getX();
2729 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002730 }
Selim Cinek67b22602014-03-10 15:40:16 +01002731 }
2732
Selim Cinekef5127e2015-12-21 16:55:58 -08002733 public void setChildTransferInProgress(boolean childTransferInProgress) {
2734 mChildTransferInProgress = childTransferInProgress;
2735 }
2736
Christoph Studer068f5922014-04-08 17:43:07 -04002737 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002738 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002739 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002740 // we only call our internal methods if this is actually a removal and not just a
2741 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002742 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002743 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002744 }
2745 }
2746
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002747 @Override
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002748 public void cleanUpViewState(View child) {
2749 if (child == mTranslatingParentView) {
2750 mTranslatingParentView = null;
2751 }
2752 mCurrentStackScrollState.removeViewStateForView(child);
2753 }
2754
Selim Cinek197823d2016-03-24 13:06:00 -07002755 @Override
2756 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2757 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2758 if (disallowIntercept) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002759 cancelLongPress();
Selim Cinek197823d2016-03-24 13:06:00 -07002760 }
2761 }
2762
Selim Cineka5703182016-05-11 21:23:16 -04002763 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002764 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002765 // This is only a position change, don't do anything special
2766 return;
2767 }
Selim Cinekd1395642016-04-28 12:22:42 -07002768 ExpandableView expandableView = (ExpandableView) child;
2769 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002770 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002771 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002772 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002773 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002774 if (!mSwipedOutViews.contains(child)
2775 || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002776 container.addTransientView(child, 0);
2777 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002778 }
2779 } else {
2780 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002781 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002782 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002783
Selim Cineke9bad242016-06-15 11:46:37 -07002784 focusNextViewIfFocused(child);
2785 }
2786
2787 private void focusNextViewIfFocused(View view) {
2788 if (view instanceof ExpandableNotificationRow) {
2789 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2790 if (row.shouldRefocusOnDismiss()) {
2791 View nextView = row.getChildAfterViewWhenDismissed();
2792 if (nextView == null) {
2793 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2794 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2795 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002796 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002797 }
2798 if (nextView != null) {
2799 nextView.requestAccessibilityFocus();
2800 }
2801 }
2802 }
2803
Selim Cinekc27437b2014-05-14 10:23:33 +02002804 }
2805
Selim Cinekb5605e52015-02-20 18:21:41 +01002806 private boolean isChildInGroup(View child) {
2807 return child instanceof ExpandableNotificationRow
2808 && mGroupManager.isChildInGroupWithSummary(
2809 ((ExpandableNotificationRow) child).getStatusBarNotification());
2810 }
2811
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002812 /**
2813 * Generate a remove animation for a child view.
2814 *
2815 * @param child The view to generate the remove animation for.
2816 * @return Whether an animation was generated.
2817 */
2818 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002819 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002820 mAddedHeadsUpChildren.remove(child);
2821 return false;
2822 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002823 if (isClickedHeadsUp(child)) {
2824 // An animation is already running, add it to the Overlay
2825 mClearOverlayViewsWhenFinished.add(child);
2826 return true;
2827 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002828 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002829 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002830 // Generate Animations
2831 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002832 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002833 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002834 } else {
2835 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002836 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002837 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002838 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002839 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002840 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002841 }
2842
Selim Cinek0fccc722015-07-29 17:04:36 -07002843 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002844 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002845 }
2846
Selim Cineke0890e52015-06-17 11:17:08 -07002847 /**
2848 * Remove a removed child view from the heads up animations if it was just added there
2849 *
2850 * @return whether any child was removed from the list to animate
2851 */
2852 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2853 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002854 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2855 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002856 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002857 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002858 mTmpList.add(eventPair);
2859 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002860 }
2861 }
Selim Cineke0890e52015-06-17 11:17:08 -07002862 if (hasAddEvent) {
2863 // This child was just added lets remove all events.
2864 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002865 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002866 }
2867 mTmpList.clear();
2868 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002869 }
2870
Selim Cinek572bbd42014-04-25 16:43:27 +02002871 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002872 * @param child the child to query
2873 * @return whether a view is not a top level child but a child notification and that group is
2874 * not expanded
2875 */
2876 private boolean isChildInInvisibleGroup(View child) {
2877 if (child instanceof ExpandableNotificationRow) {
2878 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2879 ExpandableNotificationRow groupSummary =
2880 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2881 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002882 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002883 }
2884 }
2885 return false;
2886 }
2887
2888 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002889 * Updates the scroll position when a child was removed
2890 *
2891 * @param removedChild the removed child
2892 */
Selim Cinek61633a82016-01-25 15:54:10 -08002893 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002894 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002895 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2896 int padding;
2897 if (increasedPaddingAmount >= 0) {
2898 padding = (int) NotificationUtils.interpolate(
2899 mPaddingBetweenElements,
2900 mIncreasedPaddingBetweenElements,
2901 increasedPaddingAmount);
2902 } else {
2903 padding = (int) NotificationUtils.interpolate(
2904 0,
2905 mPaddingBetweenElements,
2906 1.0f + increasedPaddingAmount);
2907 }
Selim Cinek61633a82016-01-25 15:54:10 -08002908 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002909 int endPosition = startingPosition + childHeight;
2910 if (endPosition <= mOwnScrollY) {
2911 // This child is fully scrolled of the top, so we have to deduct its height from the
2912 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002913 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002914 } else if (startingPosition < mOwnScrollY) {
2915 // This child is currently being scrolled into, set the scroll position to the start of
2916 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002917 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002918 }
2919 }
2920
Selim Cinekd7c4e002014-07-04 18:36:42 +02002921 private int getIntrinsicHeight(View view) {
2922 if (view instanceof ExpandableView) {
2923 ExpandableView expandableView = (ExpandableView) view;
2924 return expandableView.getIntrinsicHeight();
2925 }
2926 return view.getHeight();
2927 }
2928
Selim Cinek1f624952017-06-08 19:11:50 -07002929 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002930 ExpandableNotificationRow childInGroup = null;
2931 ExpandableNotificationRow requestedRow = null;
2932 if (isChildInGroup(requestedView)) {
2933 // We're asking for a child in a group. Calculate the position of the parent first,
2934 // then within the parent.
2935 childInGroup = (ExpandableNotificationRow) requestedView;
2936 requestedView = requestedRow = childInGroup.getNotificationParent();
2937 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002938 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002939 float previousPaddingRequest = mPaddingBetweenElements;
2940 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002941 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002942 ExpandableView child = (ExpandableView) getChildAt(i);
2943 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002944 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002945 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002946 float padding;
2947 if (increasedPaddingAmount >= 0.0f) {
2948 padding = (int) NotificationUtils.interpolate(
2949 previousPaddingRequest,
2950 mIncreasedPaddingBetweenElements,
2951 increasedPaddingAmount);
2952 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002953 mPaddingBetweenElements,
2954 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002955 increasedPaddingAmount);
2956 } else {
2957 int ownPadding = (int) NotificationUtils.interpolate(
2958 0,
2959 mPaddingBetweenElements,
2960 1.0f + increasedPaddingAmount);
2961 if (previousPaddingAmount > 0.0f) {
2962 padding = (int) NotificationUtils.interpolate(
2963 ownPadding,
2964 mIncreasedPaddingBetweenElements,
2965 previousPaddingAmount);
2966 } else {
2967 padding = ownPadding;
2968 }
2969 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08002970 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002971 if (position != 0) {
2972 position += padding;
2973 }
2974 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002975 }
Adrian Roos4a579672016-05-24 16:54:37 -07002976 if (child == requestedView) {
2977 if (requestedRow != null) {
2978 position += requestedRow.getPositionOfChild(childInGroup);
2979 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002980 return position;
2981 }
Selim Cinek61633a82016-01-25 15:54:10 -08002982 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02002983 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002984 }
2985 }
2986 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02002987 }
2988
2989 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002990 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02002991 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002992 onViewAddedInternal(child);
2993 }
2994
Selim Cinek614576e2016-01-20 10:54:09 -08002995 private void updateFirstAndLastBackgroundViews() {
2996 ActivatableNotificationView firstChild = getFirstChildWithBackground();
2997 ActivatableNotificationView lastChild = getLastChildWithBackground();
2998 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek29aab962018-02-27 17:05:45 -08002999 mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
3000 mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
Selim Cinek614576e2016-01-20 10:54:09 -08003001 } else {
3002 mAnimateNextBackgroundTop = false;
3003 mAnimateNextBackgroundBottom = false;
3004 }
3005 mFirstVisibleBackgroundChild = firstChild;
3006 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08003007 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek29aab962018-02-27 17:05:45 -08003008 mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
3009 mLastVisibleBackgroundChild);
Selim Cinek515b2032017-11-15 10:20:19 -08003010 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003011 }
3012
Selim Cinekb5605e52015-02-20 18:21:41 +01003013 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003014 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02003015 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003016 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003017 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003018 updateChronometerForChild(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003019 }
3020
Selim Cinekd06c41c2015-07-06 14:51:36 -07003021 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04003022 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003023 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04003024 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003025 }
3026 }
3027
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003028 @Override
Selim Cinekd1395642016-04-28 12:22:42 -07003029 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
3030 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003031 }
3032
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003033 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003034 public void notifyGroupChildAdded(View row) {
3035 onViewAddedInternal(row);
3036 }
3037
Jorim Jaggi75c95042014-05-16 19:09:59 +02003038 public void setAnimationsEnabled(boolean animationsEnabled) {
3039 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003040 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003041 if (!animationsEnabled) {
3042 mSwipedOutViews.clear();
3043 mChildrenToRemoveAnimated.clear();
3044 clearTemporaryViewsInGroup(this);
3045 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003046 }
3047
3048 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003049 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003050 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003051 int childCount = getChildCount();
3052 for (int i = 0; i < childCount; i++) {
3053 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003054 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003055 updateAnimationState(running, child);
3056 }
3057 }
3058
Selim Cinek51ae05d2014-09-09 15:51:38 +02003059 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003060 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003061 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003062 }
3063
Selim Cinek2627d722018-01-19 12:16:49 -08003064 @Override
3065 public void setExpandingNotification(ExpandableNotificationRow row) {
3066 mAmbientState.setExpandingNotification(row);
3067 requestChildrenUpdate();
3068 }
3069
3070 @Override
Selim Cinek8875de12018-03-22 10:14:32 -07003071 public void bindRow(ExpandableNotificationRow row) {
3072 row.setHeadsUpAnimatingAwayListener(animatingAway
3073 -> mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway));
3074 }
3075
3076 @Override
Selim Cinek2627d722018-01-19 12:16:49 -08003077 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3078 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3079 requestChildrenUpdate();
3080 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003081
Selim Cinekcab4a602014-09-03 14:47:57 +02003082 private void updateAnimationState(boolean running, View child) {
3083 if (child instanceof ExpandableNotificationRow) {
3084 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3085 row.setIconAnimationRunning(running);
3086 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003087 }
3088
3089 public boolean isAddOrRemoveAnimationPending() {
3090 return mNeedsAnimation
3091 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3092 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003093
3094 @Override
Jorim Jaggif6411742014-08-05 17:10:43 +00003095 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003096 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003097 // Generate Animations
3098 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003099 if (fromMoreCard) {
3100 mFromMoreCardAdditions.add(child);
3101 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003102 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003103 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003104 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003105 mAddedHeadsUpChildren.add(child);
3106 mChildrenToAddAnimated.remove(child);
3107 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003108 }
3109
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003110 @Override
Selim Cinek572bbd42014-04-25 16:43:27 +02003111 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003112 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003113
3114 if (currentIndex == -1) {
3115 boolean isTransient = false;
3116 if (child instanceof ExpandableNotificationRow
3117 && ((ExpandableNotificationRow)child).getTransientContainer() != null) {
3118 isTransient = true;
3119 }
3120 Log.e(TAG, "Attempting to re-position "
3121 + (isTransient ? "transient" : "")
3122 + " view {"
3123 + child
3124 + "}");
3125 return;
3126 }
3127
Dan Sandlereceda3d2014-07-21 15:35:01 -04003128 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003129 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08003130 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003131 removeView(child);
3132 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08003133 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003134 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003135 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003136 mChildrenChangingPositions.add(child);
3137 mNeedsAnimation = true;
3138 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003139 }
3140 }
3141
Selim Cinekf4c19962014-05-01 21:55:31 +02003142 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003143 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003144 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003145 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003146 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003147 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003148 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003149 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
3150 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003151 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003152 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003153 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003154 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003155 } else {
3156 applyCurrentState();
3157 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003158 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003159 }
3160
3161 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003162 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003163 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003164 generateChildAdditionEvents();
3165 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02003166 generateSnapBackEvents();
3167 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003168 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003169 generateActivateEvent();
3170 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003171 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003172 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003173 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003174 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003175 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003176 generateAnimateEverythingEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003177 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003178 }
3179
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003180 private void generateHeadsUpAnimationEvents() {
3181 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003182 ExpandableNotificationRow row = eventPair.first;
3183 boolean isHeadsUp = eventPair.second;
3184 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3185 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003186 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003187 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003188 type = row.wasJustClicked()
3189 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3190 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003191 if (row.isChildInGroup()) {
3192 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003193 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003194 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003195 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08003196 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04003197 if (viewState == null) {
3198 // A view state was never generated for this view, so we don't need to animate
3199 // this. This may happen with notification children.
3200 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003201 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003202 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3203 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3204 // Our custom add animation
3205 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3206 } else {
3207 // Normal add animation
3208 type = AnimationEvent.ANIMATION_TYPE_ADD;
3209 }
3210 onBottom = !pinnedAndClosed;
3211 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003212 }
3213 AnimationEvent event = new AnimationEvent(row, type);
3214 event.headsUpFromBottom = onBottom;
3215 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003216 }
3217 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003218 mAddedHeadsUpChildren.clear();
3219 }
3220
Selim Cinekbbcebde2016-11-09 18:28:20 -08003221 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003222 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3223 return false;
3224 }
3225 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003226 }
3227
Selim Cinekb5605e52015-02-20 18:21:41 +01003228 private void generateGroupExpansionEvent() {
3229 // Generate a group expansion/collapsing event if there is such a group at all
3230 if (mExpandedGroupView != null) {
3231 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3232 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3233 mExpandedGroupView = null;
3234 }
3235 }
3236
Selim Cineka5e211b2014-08-11 17:35:48 +02003237 private void generateViewResizeEvent() {
3238 if (mNeedViewResizeAnimation) {
3239 mAnimationEvents.add(
3240 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3241 }
3242 mNeedViewResizeAnimation = false;
3243 }
3244
Selim Cinekeb973562014-05-02 17:07:49 +02003245 private void generateSnapBackEvents() {
3246 for (View child : mSnappedBackChildren) {
3247 mAnimationEvents.add(new AnimationEvent(child,
3248 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3249 }
3250 mSnappedBackChildren.clear();
3251 }
3252
3253 private void generateDragEvents() {
3254 for (View child : mDragAnimPendingChildren) {
3255 mAnimationEvents.add(new AnimationEvent(child,
3256 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3257 }
3258 mDragAnimPendingChildren.clear();
3259 }
3260
Selim Cinek572bbd42014-04-25 16:43:27 +02003261 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003262 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003263 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003264
3265 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003266 float removedTranslation = child.getTranslationY();
3267 boolean ignoreChildren = true;
3268 if (child instanceof ExpandableNotificationRow) {
3269 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3270 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3271 removedTranslation = row.getTranslationWhenRemoved();
3272 ignoreChildren = false;
3273 }
Selim Cinek51052042017-07-04 12:07:55 +02003274 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003275 }
Selim Cinek51052042017-07-04 12:07:55 +02003276 if (!childWasSwipedOut) {
3277 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003278 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Selim Cinek51052042017-07-04 12:07:55 +02003279 }
3280 int animationType = childWasSwipedOut
3281 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3282 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3283 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003284 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3285 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003286 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003287 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003288 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003289 mChildrenToRemoveAnimated.clear();
3290 }
3291
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003292 private void generatePositionChangeEvents() {
3293 for (View child : mChildrenChangingPositions) {
3294 mAnimationEvents.add(new AnimationEvent(child,
3295 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3296 }
3297 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003298 if (mGenerateChildOrderChangedEvent) {
3299 mAnimationEvents.add(new AnimationEvent(null,
3300 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3301 mGenerateChildOrderChangedEvent = false;
3302 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003303 }
3304
Selim Cinek572bbd42014-04-25 16:43:27 +02003305 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003306 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003307 if (mFromMoreCardAdditions.contains(child)) {
3308 mAnimationEvents.add(new AnimationEvent(child,
3309 AnimationEvent.ANIMATION_TYPE_ADD,
3310 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3311 } else {
3312 mAnimationEvents.add(new AnimationEvent(child,
3313 AnimationEvent.ANIMATION_TYPE_ADD));
3314 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003315 }
3316 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003317 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003318 }
3319
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003320 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003321 if (mTopPaddingNeedsAnimation) {
3322 mAnimationEvents.add(
3323 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
3324 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003325 mTopPaddingNeedsAnimation = false;
3326 }
3327
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003328 private void generateActivateEvent() {
3329 if (mActivateNeedsAnimation) {
3330 mAnimationEvents.add(
3331 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3332 }
3333 mActivateNeedsAnimation = false;
3334 }
3335
Selim Cinekd9acca52014-09-01 22:33:25 +02003336 private void generateAnimateEverythingEvent() {
3337 if (mEverythingNeedsAnimation) {
3338 mAnimationEvents.add(
3339 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3340 }
3341 mEverythingNeedsAnimation = false;
3342 }
3343
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003344 private void generateDimmedEvent() {
3345 if (mDimmedNeedsAnimation) {
3346 mAnimationEvents.add(
3347 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3348 }
3349 mDimmedNeedsAnimation = false;
3350 }
3351
Jorim Jaggiae441282014-08-01 02:45:18 +02003352 private void generateHideSensitiveEvent() {
3353 if (mHideSensitiveNeedsAnimation) {
3354 mAnimationEvents.add(
3355 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3356 }
3357 mHideSensitiveNeedsAnimation = false;
3358 }
3359
John Spurlockbf370992014-06-17 13:58:31 -04003360 private void generateDarkEvent() {
3361 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003362 AnimationEvent ev = new AnimationEvent(null,
3363 AnimationEvent.ANIMATION_TYPE_DARK,
3364 new AnimationFilter()
3365 .animateDark()
3366 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003367 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3368 mAnimationEvents.add(ev);
Lucas Dupin16cfe452018-02-08 13:14:50 -08003369 startBackgroundFade();
John Spurlockbf370992014-06-17 13:58:31 -04003370 }
3371 mDarkNeedsAnimation = false;
3372 }
3373
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003374 private void generateGoToFullShadeEvent() {
3375 if (mGoToFullShadeNeedsAnimation) {
3376 mAnimationEvents.add(
3377 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3378 }
3379 mGoToFullShadeNeedsAnimation = false;
3380 }
3381
Selim Cinek67b22602014-03-10 15:40:16 +01003382 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003383 if (!isScrollingEnabled()) {
3384 return false;
3385 }
Selim Cinek67b22602014-03-10 15:40:16 +01003386 /*
3387 * This method JUST determines whether we want to intercept the motion.
3388 * If we return true, onMotionEvent will be called and we do the actual
3389 * scrolling there.
3390 */
3391
3392 /*
3393 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003394 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003395 * motion.
3396 */
3397 final int action = ev.getAction();
3398 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3399 return true;
3400 }
3401
Selim Cinek67b22602014-03-10 15:40:16 +01003402 switch (action & MotionEvent.ACTION_MASK) {
3403 case MotionEvent.ACTION_MOVE: {
3404 /*
3405 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003406 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003407 */
3408
3409 /*
3410 * Locally do absolute value. mLastMotionY is set to the y value
3411 * of the down event.
3412 */
3413 final int activePointerId = mActivePointerId;
3414 if (activePointerId == INVALID_POINTER) {
3415 // If we don't have a valid id, the touch down wasn't on content.
3416 break;
3417 }
3418
3419 final int pointerIndex = ev.findPointerIndex(activePointerId);
3420 if (pointerIndex == -1) {
3421 Log.e(TAG, "Invalid pointerId=" + activePointerId
3422 + " in onInterceptTouchEvent");
3423 break;
3424 }
3425
3426 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003427 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003428 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003429 final int xDiff = Math.abs(x - mDownX);
3430 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003431 setIsBeingDragged(true);
3432 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003433 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003434 initVelocityTrackerIfNotExists();
3435 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003436 }
3437 break;
3438 }
3439
3440 case MotionEvent.ACTION_DOWN: {
3441 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303442 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07003443 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01003444 setIsBeingDragged(false);
3445 recycleVelocityTracker();
3446 break;
3447 }
3448
3449 /*
3450 * Remember location of down touch.
3451 * ACTION_DOWN always refers to pointer index 0.
3452 */
3453 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003454 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003455 mActivePointerId = ev.getPointerId(0);
3456
3457 initOrResetVelocityTracker();
3458 mVelocityTracker.addMovement(ev);
3459 /*
3460 * If being flinged and user touches the screen, initiate drag;
3461 * otherwise don't. mScroller.isFinished should be false when
3462 * being flinged.
3463 */
3464 boolean isBeingDragged = !mScroller.isFinished();
3465 setIsBeingDragged(isBeingDragged);
3466 break;
3467 }
3468
3469 case MotionEvent.ACTION_CANCEL:
3470 case MotionEvent.ACTION_UP:
3471 /* Release the drag */
3472 setIsBeingDragged(false);
3473 mActivePointerId = INVALID_POINTER;
3474 recycleVelocityTracker();
3475 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08003476 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01003477 }
3478 break;
3479 case MotionEvent.ACTION_POINTER_UP:
3480 onSecondaryPointerUp(ev);
3481 break;
3482 }
3483
3484 /*
3485 * The only time we want to intercept motion events is if we are in the
3486 * drag mode.
3487 */
3488 return mIsBeingDragged;
3489 }
3490
Muyuan Li333a4fc2016-04-16 17:13:46 -07003491 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3492 return new StackScrollAlgorithm(context);
3493 }
3494
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003495 /**
3496 * @return Whether the specified motion event is actually happening over the content.
3497 */
3498 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003499 return isInContentBounds(event.getY());
3500 }
3501
3502 /**
3503 * @return Whether a y coordinate is inside the content.
3504 */
3505 public boolean isInContentBounds(float y) {
3506 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003507 }
3508
Selim Cinek67b22602014-03-10 15:40:16 +01003509 private void setIsBeingDragged(boolean isDragged) {
3510 mIsBeingDragged = isDragged;
3511 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003512 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003513 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003514 }
3515 }
3516
3517 @Override
3518 public void onWindowFocusChanged(boolean hasWindowFocus) {
3519 super.onWindowFocusChanged(hasWindowFocus);
3520 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003521 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003522 }
3523 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003524
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003525 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003526 public void clearChildFocus(View child) {
3527 super.clearChildFocus(child);
3528 if (mForcedScroll == child) {
3529 mForcedScroll = null;
3530 }
3531 }
3532
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003533 public void requestDisallowLongPress() {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003534 cancelLongPress();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003535 }
3536
Adrian Roosfa139752016-04-27 09:59:08 -07003537 public void requestDisallowDismiss() {
3538 mDisallowDismissInThisMotion = true;
3539 }
3540
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003541 public void cancelLongPress() {
3542 mSwipeHelper.cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02003543 }
3544
Selim Cinekfab078b2014-03-27 22:45:58 +01003545 @Override
3546 public boolean isScrolledToTop() {
3547 return mOwnScrollY == 0;
3548 }
3549
3550 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003551 public boolean isScrolledToBottom() {
3552 return mOwnScrollY >= getScrollRange();
3553 }
3554
3555 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003556 public View getHostView() {
3557 return this;
3558 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003559
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003560 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003561 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003562 }
3563
Mady Mellorc2dbe492017-03-30 13:22:03 -07003564 public void checkSnoozeLeavebehind() {
3565 if (mCheckForLeavebehind) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09003566 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
3567 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3568 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003569 mCheckForLeavebehind = false;
3570 }
3571 }
3572
3573 public void resetCheckSnoozeLeavebehind() {
3574 mCheckForLeavebehind = true;
3575 }
3576
Selim Cinek1685e632014-04-08 02:27:49 +02003577 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003578 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003579 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003580 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02003581 }
3582
3583 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003584 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07003585 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08003586 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003587 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003588 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003589 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003590 clearTemporaryViews();
3591 clearUserLockedViews();
3592 }
3593 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01003594
Selim Cinek5b1591a2017-07-03 17:05:01 +02003595 private void clearUserLockedViews() {
3596 for (int i = 0; i < getChildCount(); i++) {
3597 ExpandableView child = (ExpandableView) getChildAt(i);
3598 if (child instanceof ExpandableNotificationRow) {
3599 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3600 row.setUserLocked(false);
3601 }
3602 }
3603 }
3604
3605 private void clearTemporaryViews() {
3606 // lets make sure nothing is in the overlay / transient anymore
Rohan Shah8ee53652018-04-05 11:13:50 -07003607 clearTemporaryViewsInGroup(this);
Selim Cinek5b1591a2017-07-03 17:05:01 +02003608 for (int i = 0; i < getChildCount(); i++) {
3609 ExpandableView child = (ExpandableView) getChildAt(i);
3610 if (child instanceof ExpandableNotificationRow) {
3611 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Rohan Shah8ee53652018-04-05 11:13:50 -07003612 clearTemporaryViewsInGroup(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003613 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003614 }
Selim Cinek1685e632014-04-08 02:27:49 +02003615 }
3616
Rohan Shah8ee53652018-04-05 11:13:50 -07003617 private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003618 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003619 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003620 }
Selim Cineka5703182016-05-11 21:23:16 -04003621 if (viewGroup != null) {
3622 viewGroup.getOverlay().clear();
3623 }
Selim Cinekd1395642016-04-28 12:22:42 -07003624 }
3625
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003626 public void onPanelTrackingStarted() {
3627 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003628 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003629 }
3630 public void onPanelTrackingStopped() {
3631 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003632 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003633 }
3634
Selim Cinekb24e0a92015-06-09 20:17:30 -07003635 public void resetScrollPosition() {
3636 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003637 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003638 }
3639
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003640 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003641 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003642 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003643 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003644 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003645 if (!mIsExpanded) {
3646 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003647 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08003648 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003649 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003650 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003651 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003652 }
3653 }
3654
3655 private void updateChronometers() {
3656 int childCount = getChildCount();
3657 for (int i = 0; i < childCount; i++) {
3658 updateChronometerForChild(getChildAt(i));
3659 }
3660 }
3661
3662 private void updateChronometerForChild(View child) {
3663 if (child instanceof ExpandableNotificationRow) {
3664 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3665 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003666 }
Selim Cinek1685e632014-04-08 02:27:49 +02003667 }
3668
Jorim Jaggibe565df2014-04-28 17:51:23 +02003669 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003670 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003671 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003672 updateScrollPositionOnExpandInBottom(view);
3673 clampScrollPosition();
Lucas Dupin60661a62018-04-12 10:50:13 -07003674 notifyHeightChangeListener(view, needsAnimation);
Selim Cinekbc243a92016-09-27 16:35:13 -07003675 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3676 ? (ExpandableNotificationRow) view
3677 : null;
3678 if (row != null && (row == mFirstVisibleBackgroundChild
3679 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3680 updateAlgorithmLayoutMinHeight();
3681 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003682 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003683 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003684 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003685 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003686 }
3687
Selim Cineka5e211b2014-08-11 17:35:48 +02003688 @Override
3689 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003690 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003691 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003692 }
3693
Selim Cinekf7a14c02014-07-07 14:01:46 +02003694 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07003695 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02003696 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003697 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003698 if (row.isSummaryWithChildren()) {
3699 return;
3700 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003701 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003702 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003703 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003704 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003705 }
Selim Cinekdb167372016-11-17 15:41:17 -08003706 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Anthony Chen9e05d462017-04-07 10:10:21 -07003707 if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08003708 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3709 }
3710 if (endPosition > layoutEnd) {
3711 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003712 mDisallowScrollingInThisMotion = true;
3713 }
3714 }
3715 }
3716 }
3717
Jorim Jaggibe565df2014-04-28 17:51:23 +02003718 public void setOnHeightChangedListener(
3719 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3720 this.mOnHeightChangedListener = mOnHeightChangedListener;
3721 }
3722
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003723 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3724 mOnEmptySpaceClickListener = listener;
3725 }
3726
Selim Cinek572bbd42014-04-25 16:43:27 +02003727 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003728 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003729 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003730 runAnimationFinishedRunnables();
Selim Cinek0fccc722015-07-29 17:04:36 -07003731 clearViewOverlays();
Selim Cinek8fc78752016-07-13 14:34:56 -07003732 clearHeadsUpDisappearRunning();
3733 }
3734
3735 private void clearHeadsUpDisappearRunning() {
3736 for (int i = 0; i < getChildCount(); i++) {
3737 View view = getChildAt(i);
3738 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003739 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003740 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003741 if (row.isSummaryWithChildren()) {
3742 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003743 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003744 }
3745 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003746 }
3747 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003748 }
3749
3750 private void clearViewOverlays() {
3751 for (View view : mClearOverlayViewsWhenFinished) {
Selim Cineka5703182016-05-11 21:23:16 -04003752 StackStateAnimator.removeFromOverlay(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003753 }
Selim Cinekc8df0642017-02-23 17:01:04 -08003754 mClearOverlayViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003755 }
3756
3757 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003758 for (Runnable runnable : mAnimationFinishedRunnables) {
3759 runnable.run();
3760 }
3761 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003762 }
3763
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003764 /**
3765 * See {@link AmbientState#setDimmed}.
3766 */
3767 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07003768 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003769 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003770 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003771 mDimmedNeedsAnimation = true;
3772 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003773 animateDimmed(dimmed);
3774 } else {
3775 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003776 }
3777 requestChildrenUpdate();
3778 }
3779
Selim Cinek8a9308d2017-08-24 09:31:08 -07003780 @VisibleForTesting
3781 boolean isDimmed() {
3782 return mAmbientState.isDimmed();
3783 }
3784
Selim Cinekd35c2792016-01-21 13:20:57 -08003785 private void setDimAmount(float dimAmount) {
3786 mDimAmount = dimAmount;
3787 updateBackgroundDimming();
3788 }
3789
3790 private void animateDimmed(boolean dimmed) {
3791 if (mDimAnimator != null) {
3792 mDimAnimator.cancel();
3793 }
3794 float target = dimmed ? 1.0f : 0.0f;
3795 if (target == mDimAmount) {
3796 return;
3797 }
3798 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3799 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3800 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3801 mDimAnimator.addListener(mDimEndListener);
3802 mDimAnimator.addUpdateListener(mDimUpdateListener);
3803 mDimAnimator.start();
3804 }
3805
Jorim Jaggiae441282014-08-01 02:45:18 +02003806 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3807 if (hideSensitive != mAmbientState.isHideSensitive()) {
3808 int childCount = getChildCount();
3809 for (int i = 0; i < childCount; i++) {
3810 ExpandableView v = (ExpandableView) getChildAt(i);
3811 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3812 }
3813 mAmbientState.setHideSensitive(hideSensitive);
3814 if (animate && mAnimationsEnabled) {
3815 mHideSensitiveNeedsAnimation = true;
3816 mNeedsAnimation = true;
3817 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08003818 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02003819 requestChildrenUpdate();
3820 }
3821 }
3822
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003823 /**
3824 * See {@link AmbientState#setActivatedChild}.
3825 */
Selim Cineka32ab602014-06-11 15:06:01 +02003826 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003827 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003828 if (mAnimationsEnabled) {
3829 mActivateNeedsAnimation = true;
3830 mNeedsAnimation = true;
3831 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003832 requestChildrenUpdate();
3833 }
3834
Selim Cineka32ab602014-06-11 15:06:01 +02003835 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003836 return mAmbientState.getActivatedChild();
3837 }
3838
Selim Cinek572bbd42014-04-25 16:43:27 +02003839 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003840 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003841 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003842 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02003843 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003844 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003845 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003846 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003847 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003848 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08003849 }
3850
3851 private void updateViewShadows() {
3852 // we need to work around an issue where the shadow would not cast between siblings when
3853 // their z difference is between 0 and 0.1
3854
3855 // Lefts first sort by Z difference
3856 for (int i = 0; i < getChildCount(); i++) {
3857 ExpandableView child = (ExpandableView) getChildAt(i);
3858 if (child.getVisibility() != GONE) {
3859 mTmpSortedChildren.add(child);
3860 }
3861 }
3862 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3863
3864 // Now lets update the shadow for the views
3865 ExpandableView previous = null;
3866 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3867 ExpandableView expandableView = mTmpSortedChildren.get(i);
3868 float translationZ = expandableView.getTranslationZ();
3869 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3870 float diff = otherZ - translationZ;
3871 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3872 // There is no fake shadow to be drawn
3873 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3874 } else {
3875 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003876 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3877 expandableView.setFakeShadowIntensity(
3878 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003879 previous.getOutlineAlpha(), (int) yLocation,
3880 previous.getOutlineTranslation());
3881 }
3882 previous = expandableView;
3883 }
3884
3885 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003886 }
3887
Lucas Dupine17ce522017-07-17 15:45:06 -07003888 /**
3889 * Update colors of "dismiss" and "empty shade" views.
3890 *
3891 * @param lightTheme True if light theme should be used.
3892 */
3893 public void updateDecorViews(boolean lightTheme) {
3894 if (lightTheme == mUsingLightTheme) {
3895 return;
3896 }
3897 mUsingLightTheme = lightTheme;
3898 Context context = new ContextThemeWrapper(mContext,
3899 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
3900 final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04003901 mFooterView.setTextColor(textColor);
Lucas Dupine17ce522017-07-17 15:45:06 -07003902 mEmptyShadeView.setTextColor(textColor);
3903 }
3904
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003905 public void goToFullShade(long delay) {
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04003906 if (mFooterView != null) {
3907 mFooterView.setInvisible();
Anthony Chen5e3742e2017-04-07 14:28:44 -07003908 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003909 mEmptyShadeView.setInvisible();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003910 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003911 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003912 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003913 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003914 }
3915
Selim Cinek1408eb52014-06-02 14:45:38 +02003916 public void cancelExpandHelper() {
3917 mExpandHelper.cancel();
3918 }
3919
3920 public void setIntrinsicPadding(int intrinsicPadding) {
3921 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07003922 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02003923 }
3924
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003925 public int getIntrinsicPadding() {
3926 return mIntrinsicPadding;
3927 }
3928
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003929 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003930 * @return the y position of the first notification
3931 */
3932 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003933 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003934 }
3935
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003936 @Override
3937 public boolean shouldDelayChildPressedState() {
3938 return true;
3939 }
3940
Jorim Jaggi457cc352014-06-02 22:47:42 +02003941 /**
John Spurlockbf370992014-06-17 13:58:31 -04003942 * See {@link AmbientState#setDark}.
3943 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003944 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02003945 if (mAmbientState.isDark() == dark) {
3946 return;
3947 }
John Spurlockbf370992014-06-17 13:58:31 -04003948 mAmbientState.setDark(dark);
3949 if (animate && mAnimationsEnabled) {
3950 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003951 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
John Spurlockbf370992014-06-17 13:58:31 -04003952 mNeedsAnimation = true;
Selim Cinek6811d722016-01-19 17:53:12 -08003953 } else {
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08003954 setDarkAmount(dark ? 1f : 0f);
Selim Cinek6811d722016-01-19 17:53:12 -08003955 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08003956 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08003957 requestChildrenUpdate();
3958 applyCurrentBackgroundBounds();
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003959 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003960 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04003961 }
3962
Lucas Dupin0cd882f2018-01-30 12:19:49 -08003963 private void updateAntiBurnInTranslation() {
Lucas Dupin15475702018-04-04 14:32:53 -07003964 setTranslationX(mAntiBurnInOffsetX * mDarkAmount);
Lucas Dupin0cd882f2018-01-30 12:19:49 -08003965 }
3966
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003967 /**
3968 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
3969 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
3970 * {@link #mAmbientState}'s dark mode is toggled.
3971 */
3972 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08003973 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07003974 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003975 }
3976
Lucas Dupind285cf02018-01-18 09:18:23 -08003977 private void setDarkAmount(float darkAmount) {
3978 mDarkAmount = darkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -07003979 boolean wasFullyDark = mAmbientState.isFullyDark();
3980 mAmbientState.setDarkAmount(darkAmount);
3981 if (mAmbientState.isFullyDark() != wasFullyDark) {
Lucas Dupin16cfe452018-02-08 13:14:50 -08003982 updateContentHeight();
Lucas Dupinb561eda2018-04-09 17:25:04 -07003983 DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
3984 if (mAmbientState.isFullyDark() && dozeParameters.shouldControlScreenOff()) {
3985 mShelf.fadeInTranslating();
3986 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08003987 }
Lucas Dupin60661a62018-04-12 10:50:13 -07003988 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07003989 updateBackgroundDimming();
Lucas Dupin15475702018-04-04 14:32:53 -07003990 updateAntiBurnInTranslation();
Lucas Dupinb561eda2018-04-09 17:25:04 -07003991 requestChildrenUpdate();
Selim Cinek972123d2016-05-03 14:25:58 -07003992 }
3993
Lucas Dupind285cf02018-01-18 09:18:23 -08003994 public float getDarkAmount() {
3995 return mDarkAmount;
Selim Cinek972123d2016-05-03 14:25:58 -07003996 }
3997
Lucas Dupin16cfe452018-02-08 13:14:50 -08003998 private void startBackgroundFade() {
3999 ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount,
4000 mAmbientState.isDark() ? 1f : 0);
Adrian Roos28f90c72017-05-08 17:24:26 -07004001 fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
Selim Cinek972123d2016-05-03 14:25:58 -07004002 fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
4003 fadeAnimator.start();
4004 }
4005
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004006 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07004007 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004008 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4009 }
4010 if (screenLocation.y > getBottomMostNotificationBottom()) {
4011 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
4012 }
4013 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
4014 if (child != null) {
4015 return getNotGoneIndex(child);
4016 } else {
4017 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4018 }
4019 }
4020
4021 private int getNotGoneIndex(View child) {
4022 int count = getChildCount();
4023 int notGoneIndex = 0;
4024 for (int i = 0; i < count; i++) {
4025 View v = getChildAt(i);
4026 if (child == v) {
4027 return notGoneIndex;
4028 }
4029 if (v.getVisibility() != View.GONE) {
4030 notGoneIndex++;
4031 }
4032 }
4033 return -1;
4034 }
4035
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004036 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004037 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004038 if (mFooterView != null) {
4039 index = indexOfChild(mFooterView);
4040 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004041 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004042 mFooterView = footerView;
4043 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004044 }
4045
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004046 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004047 int index = -1;
4048 if (mEmptyShadeView != null) {
4049 index = indexOfChild(mEmptyShadeView);
4050 removeView(mEmptyShadeView);
4051 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004052 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004053 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004054 }
4055
4056 public void updateEmptyShadeView(boolean visible) {
4057 int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
4058 int newVisibility = visible ? VISIBLE : GONE;
Beverlybadf6e42018-04-18 17:32:56 -04004059
4060 boolean changedVisibility = oldVisibility != newVisibility;
4061 if (changedVisibility || newVisibility != GONE) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02004062 if (newVisibility != GONE) {
Beverlybadf6e42018-04-18 17:32:56 -04004063 int oldText = mEmptyShadeView.getTextResource();
4064 int newText;
Julia Reynolds9cf17562018-03-14 09:49:35 -04004065 if (mStatusBar.areNotificationsHidden()) {
Beverlybadf6e42018-04-18 17:32:56 -04004066 newText = R.string.dnd_suppressing_shade_text;
Julia Reynolds9cf17562018-03-14 09:49:35 -04004067 } else {
Beverlybadf6e42018-04-18 17:32:56 -04004068 newText = R.string.empty_shade_text;
Julia Reynolds9cf17562018-03-14 09:49:35 -04004069 }
Beverlybadf6e42018-04-18 17:32:56 -04004070 if (changedVisibility || !Objects.equals(oldText, newText)) {
4071 mEmptyShadeView.setText(newText);
4072 showFooterView(mEmptyShadeView);
4073 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004074 } else {
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004075 hideFooterView(mEmptyShadeView, true);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004076 }
4077 }
4078 }
4079
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004080 public void updateFooterView(boolean visible, boolean showDismissView) {
4081 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004082 return;
4083 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004084 int oldVisibility = mFooterView.willBeGone() ? GONE : mFooterView.getVisibility();
Dan Sandlereceda3d2014-07-21 15:35:01 -04004085 int newVisibility = visible ? VISIBLE : GONE;
4086 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02004087 if (newVisibility != GONE) {
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004088 showFooterView(mFooterView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004089 } else {
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004090 hideFooterView(mFooterView, mFooterView.isButtonVisible());
Dan Sandlereceda3d2014-07-21 15:35:01 -04004091 }
4092 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004093 if (mFooterView.isSecondaryVisible() != showDismissView) {
4094 mFooterView.performSecondaryVisibilityAnimation(showDismissView);
4095 }
4096 }
4097
4098 private void showFooterView(StackScrollerDecorView footerView) {
4099 if (footerView.willBeGone()) {
4100 footerView.cancelAnimation();
4101 } else {
4102 footerView.setInvisible();
4103 }
4104 footerView.setVisibility(VISIBLE);
4105 footerView.setWillBeGone(false);
4106 updateContentHeight();
4107 notifyHeightChangeListener(footerView);
4108 }
4109
4110 private void hideFooterView(StackScrollerDecorView footerView, boolean isButtonVisible) {
4111 Runnable onHideFinishRunnable = new Runnable() {
4112 @Override
4113 public void run() {
4114 footerView.setVisibility(GONE);
4115 footerView.setWillBeGone(false);
4116 updateContentHeight();
4117 notifyHeightChangeListener(footerView);
4118 }
4119 };
4120 if (isButtonVisible && mIsExpanded && mAnimationsEnabled) {
4121 footerView.setWillBeGone(true);
4122 footerView.performVisibilityAnimation(false, onHideFinishRunnable);
4123 } else {
4124 onHideFinishRunnable.run();
4125 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04004126 }
4127
4128 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4129 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004130 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004131 handleDismissAllClipping();
4132 }
4133
4134 private void handleDismissAllClipping() {
4135 final int count = getChildCount();
4136 boolean previousChildWillBeDismissed = false;
4137 for (int i = 0; i < count; i++) {
4138 ExpandableView child = (ExpandableView) getChildAt(i);
4139 if (child.getVisibility() == GONE) {
4140 continue;
4141 }
4142 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4143 child.setMinClipTopAmount(child.getClipTopAmount());
4144 } else {
4145 child.setMinClipTopAmount(0);
4146 }
4147 previousChildWillBeDismissed = canChildBeDismissed(child);
4148 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004149 }
4150
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004151 public boolean isFooterViewNotGone() {
4152 return mFooterView != null
4153 && mFooterView.getVisibility() != View.GONE
4154 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004155 }
4156
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004157 public boolean isFooterViewVisible() {
4158 return mFooterView != null && mFooterView.isVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004159 }
4160
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004161 public int getFooterViewHeight() {
4162 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004163 }
4164
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004165 public int getEmptyShadeViewHeight() {
4166 return mEmptyShadeView.getHeight();
4167 }
4168
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004169 public float getBottomMostNotificationBottom() {
4170 final int count = getChildCount();
4171 float max = 0;
4172 for (int childIdx = 0; childIdx < count; childIdx++) {
4173 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4174 if (child.getVisibility() == GONE) {
4175 continue;
4176 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004177 float bottom = child.getTranslationY() + child.getActualHeight()
4178 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004179 if (bottom > max) {
4180 max = bottom;
4181 }
4182 }
Selim Cinekd2281152015-04-10 14:37:46 -07004183 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004184 }
4185
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004186 public void setStatusBar(StatusBar statusBar) {
4187 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004188 }
4189
Selim Cinekb5605e52015-02-20 18:21:41 +01004190 public void setGroupManager(NotificationGroupManager groupManager) {
4191 this.mGroupManager = groupManager;
4192 }
4193
Selim Cinekd9acca52014-09-01 22:33:25 +02004194 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01004195 requestAnimateEverything();
4196 }
4197
4198 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004199 if (mIsExpanded && mAnimationsEnabled) {
4200 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004201 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004202 requestChildrenUpdate();
4203 }
4204 }
4205
Selim Cinek04fb2582015-06-02 19:58:09 +02004206 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004207 int childCount = getChildCount();
4208 for (int i = childCount - 1; i >= 0; i--) {
4209 ExpandableView child = (ExpandableView) getChildAt(i);
4210 if (child.getVisibility() != View.GONE) {
4211 float childTop = child.getY();
4212 if (childTop > touchY) {
4213 // we are above a notification entirely let's abort
4214 return false;
4215 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004216 boolean belowChild = touchY > childTop + child.getActualHeight()
4217 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004218 if (child == mFooterView) {
4219 if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
Selim Cinekabf60bb2015-02-20 17:36:10 +01004220 touchY - childTop)) {
4221 // We clicked on the dismiss button
4222 return false;
4223 }
4224 } else if (child == mEmptyShadeView) {
4225 // We arrived at the empty shade view, for which we accept all clicks
4226 return true;
4227 } else if (!belowChild){
4228 // We are on a child
4229 return false;
4230 }
4231 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004232 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004233 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004234 }
4235
Selim Cinekb5605e52015-02-20 18:21:41 +01004236 @Override
4237 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004238 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4239 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004240 if (animated) {
4241 mExpandedGroupView = changedRow;
4242 mNeedsAnimation = true;
4243 }
4244 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004245 if (!mGroupExpandedForMeasure) {
4246 onHeightChanged(changedRow, false /* needsAnimation */);
4247 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004248 runAfterAnimationFinished(new Runnable() {
4249 @Override
4250 public void run() {
4251 changedRow.onFinishedExpansionChange();
4252 }
4253 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004254 }
4255
4256 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004257 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004258 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004259 }
4260
Selim Cinekc22fff62016-05-20 12:44:30 -07004261 /** @hide */
4262 @Override
4263 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4264 super.onInitializeAccessibilityEventInternal(event);
4265 event.setScrollable(mScrollable);
4266 event.setScrollX(mScrollX);
4267 event.setScrollY(mOwnScrollY);
4268 event.setMaxScrollX(mScrollX);
4269 event.setMaxScrollY(getScrollRange());
4270 }
4271
4272 @Override
4273 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4274 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004275 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004276 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004277 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004278 info.addAction(
4279 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4280 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4281 }
Selim Cinekef406062016-09-29 17:33:13 -07004282 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004283 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4284 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4285 }
4286 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004287 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4288 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004289 }
4290
4291 /** @hide */
4292 @Override
4293 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4294 if (super.performAccessibilityActionInternal(action, arguments)) {
4295 return true;
4296 }
4297 if (!isEnabled()) {
4298 return false;
4299 }
4300 int direction = -1;
4301 switch (action) {
4302 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4303 // fall through
4304 case android.R.id.accessibilityActionScrollDown:
4305 direction = 1;
4306 // fall through
4307 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4308 // fall through
4309 case android.R.id.accessibilityActionScrollUp:
4310 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004311 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004312 final int targetScrollY = Math.max(0,
4313 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4314 if (targetScrollY != mOwnScrollY) {
4315 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004316 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004317 return true;
4318 }
4319 break;
4320 }
4321 return false;
4322 }
4323
Selim Cinekef5127e2015-12-21 16:55:58 -08004324 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004325 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004326 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004327 }
4328
4329 public void generateChildOrderChangedEvent() {
4330 if (mIsExpanded && mAnimationsEnabled) {
4331 mGenerateChildOrderChangedEvent = true;
4332 mNeedsAnimation = true;
4333 requestChildrenUpdate();
4334 }
4335 }
4336
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004337 @Override
4338 public int getContainerChildCount() {
4339 return getChildCount();
4340 }
4341
4342 @Override
4343 public View getContainerChildAt(int i) {
4344 return getChildAt(i);
4345 }
4346
4347 @Override
4348 public void removeContainerView(View v) {
4349 removeView(v);
4350 }
4351
4352 @Override
4353 public void addContainerView(View v) {
4354 addView(v);
4355 }
4356
Selim Cinek684a4422015-04-15 16:18:39 -07004357 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004358 mAnimationFinishedRunnables.add(runnable);
4359 }
4360
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004361 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004362 mHeadsUpManager = headsUpManager;
4363 mAmbientState.setHeadsUpManager(headsUpManager);
Selim Cinek29aab962018-02-27 17:05:45 -08004364 mHeadsUpManager.addListener(mRoundnessManager);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004365 }
4366
4367 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004368 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004369 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4370 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004371 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004372 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004373 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004374 requestChildrenUpdate();
4375 }
4376 }
4377
4378 public void setShadeExpanded(boolean shadeExpanded) {
4379 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004380 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004381 }
4382
Selim Cineka59ecc32015-04-07 10:51:49 -07004383 /**
4384 * Set the boundary for the bottom heads up position. The heads up will always be above this
4385 * position.
4386 *
4387 * @param height the height of the screen
4388 * @param bottomBarHeight the height of the bar on the bottom
4389 */
4390 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4391 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4392 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004393 requestChildrenUpdate();
4394 }
4395
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004396 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
4397 mTrackingHeadsUp = row != null;
4398 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004399 }
4400
Selim Cinekaac93252015-04-14 20:04:12 -07004401 public void setScrimController(ScrimController scrimController) {
4402 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004403 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004404 }
4405
Selim Cinekbbc580b2015-06-03 14:11:03 +02004406 public void forceNoOverlappingRendering(boolean force) {
4407 mForceNoOverlappingRendering = force;
4408 }
4409
4410 @Override
4411 public boolean hasOverlappingRendering() {
4412 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4413 }
4414
Selim Cinek6811d722016-01-19 17:53:12 -08004415 public void setAnimationRunning(boolean animationRunning) {
4416 if (animationRunning != mAnimationRunning) {
4417 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004418 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004419 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004420 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004421 }
4422 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004423 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004424 }
4425 }
4426
Selim Cinek3776fe02016-02-04 13:32:43 -08004427 public boolean isExpanded() {
4428 return mIsExpanded;
4429 }
4430
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004431 public void setPulsing(boolean pulsing, int clockBottom) {
4432 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004433 return;
4434 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004435 mPulsing = pulsing;
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004436 mClockBottom = clockBottom;
Selim Cinekebf42342017-07-13 15:46:10 +02004437 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004438 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004439 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004440 updateContentHeight();
4441 notifyHeightChangeListener(mShelf);
Adrian Roosd83e9992017-03-16 15:17:57 -07004442 requestChildrenUpdate();
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004443 }
4444
Selim Cinek07304f5222016-05-19 18:31:36 -07004445 public void setFadingOut(boolean fadingOut) {
4446 if (fadingOut != mFadingOut) {
4447 mFadingOut = fadingOut;
4448 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004449 }
4450 }
4451
Selim Cinek48ff9b42016-11-09 19:31:51 -08004452 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4453 if (mScrimController == null) {
4454 // we're not set up yet.
4455 return;
4456 }
4457 if (parentNotFullyVisible != mParentNotFullyVisible) {
4458 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004459 updateFadingState();
4460 }
4461 }
4462
4463 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004464 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004465 updateSrcDrawing();
4466 }
4467
Selim Cinek31d37b92016-04-26 09:56:42 -07004468 @Override
4469 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4470 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004471 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004472 }
4473
Selim Cinekbc243a92016-09-27 16:35:13 -07004474 public void setQsExpanded(boolean qsExpanded) {
4475 mQsExpanded = qsExpanded;
4476 updateAlgorithmLayoutMinHeight();
4477 }
4478
Selim Cinekef406062016-09-29 17:33:13 -07004479 public void setOwnScrollY(int ownScrollY) {
4480 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004481 // We still want to call the normal scrolled changed for accessibility reasons
4482 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004483 mOwnScrollY = ownScrollY;
4484 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004485 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004486 }
4487 }
4488
Selim Cinek281c2022016-10-13 19:14:43 -07004489 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004490 int index = -1;
4491 if (mShelf != null) {
4492 index = indexOfChild(mShelf);
4493 removeView(mShelf);
4494 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004495 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004496 addView(mShelf, index);
4497 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004498 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004499 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004500 }
4501
4502 public NotificationShelf getNotificationShelf() {
4503 return mShelf;
4504 }
4505
Selim Cinekad7fac02016-10-18 17:09:15 -07004506 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4507 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4508 mMaxDisplayedNotifications = maxDisplayedNotifications;
4509 updateContentHeight();
4510 notifyHeightChangeListener(mShelf);
4511 }
4512 }
4513
Selim Cinek48ff9b42016-11-09 19:31:51 -08004514 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004515 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004516 }
4517
Selim Cinekcafa87f2016-10-26 17:00:17 -07004518 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4519 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4520 updateClipping();
4521 }
4522
4523 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4524 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4525 updateClipping();
4526 }
4527
Selim Cinek355652a2016-12-07 13:32:12 -08004528 public void setStatusBarState(int statusBarState) {
4529 mStatusBarState = statusBarState;
4530 mAmbientState.setStatusBarState(statusBarState);
4531 }
4532
Selim Cinekd5ab6452016-12-08 16:34:00 -08004533 public void setExpandingVelocity(float expandingVelocity) {
4534 mAmbientState.setExpandingVelocity(expandingVelocity);
4535 }
4536
Selim Cinekfcff4c62016-12-27 14:26:06 +01004537 public float getOpeningHeight() {
4538 if (mEmptyShadeView.getVisibility() == GONE) {
4539 return getMinExpansionHeight();
4540 } else {
4541 return getAppearEndPosition();
4542 }
4543 }
4544
4545 public void setIsFullWidth(boolean isFullWidth) {
4546 mAmbientState.setPanelFullWidth(isFullWidth);
4547 }
4548
Selim Cinekec29d342017-05-05 18:31:49 -07004549 public void setUnlockHintRunning(boolean running) {
4550 mAmbientState.setUnlockHintRunning(running);
4551 }
4552
Selim Cinek5cf1d052017-06-01 17:36:46 -07004553 public void setQsCustomizerShowing(boolean isShowing) {
4554 mAmbientState.setQsCustomizerShowing(isShowing);
4555 requestChildrenUpdate();
4556 }
4557
4558 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4559 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4560 }
4561
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004562 public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
4563 mAntiBurnInOffsetX = antiBurnInOffsetX;
4564 updateAntiBurnInTranslation();
Adrian Roosdc747bd2017-06-01 16:09:15 -07004565 }
4566
Selim Cinek707e2072017-06-30 18:32:40 +02004567 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4568 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
4569 + " alpha:%f scrollY:%d]",
4570 this.getClass().getSimpleName(),
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004571 mPulsing ? "T":"f",
Selim Cinek707e2072017-06-30 18:32:40 +02004572 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4573 getVisibility() == View.VISIBLE ? "visible"
4574 : getVisibility() == View.GONE ? "gone"
4575 : "invisible",
4576 getAlpha(),
4577 mAmbientState.getScrollY()));
4578 }
4579
Lucas Dupin16cfe452018-02-08 13:14:50 -08004580 public boolean isFullyDark() {
4581 return mAmbientState.isFullyDark();
4582 }
4583
Selim Cinek7103fd42016-05-09 22:22:33 -04004584 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004585 * Add a listener whenever the expanded height changes. The first value passed as an argument
4586 * is the expanded height and the second one is the appearFraction.
4587 *
4588 * @param listener the listener to notify.
4589 */
4590 public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4591 mExpandedHeightListeners.add(listener);
4592 }
4593
4594 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07004595 * Stop a listener from listening to the expandedHeight.
4596 */
4597 public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4598 mExpandedHeightListeners.remove(listener);
4599 }
4600
4601 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004602 * A listener that is notified when the empty space below the notifications is clicked on
4603 */
4604 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004605 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004606 }
4607
4608 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004609 * A listener that gets notified when the overscroll at the top has changed.
4610 */
4611 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004612
4613 /**
4614 * Notifies a listener that the overscroll has changed.
4615 *
4616 * @param amount the amount of overscroll, in pixels
4617 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4618 * unrubberbanded motion to directly expand overscroll view (e.g expand
4619 * QS)
4620 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004621 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004622
4623 /**
4624 * Notify a listener that the scroller wants to escape from the scrolling motion and
4625 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4626 *
4627 * @param velocity The velocity that the Scroller had when over flinging
4628 * @param open Should the fling open or close the overscroll view.
4629 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004630 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004631 }
4632
Mady Mellor95d743c2017-01-10 12:05:27 -08004633 private class NotificationSwipeHelper extends SwipeHelper
4634 implements NotificationSwipeActionHelper {
4635 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004636 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004637 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004638
4639 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4640 super(swipeDirection, callback, context);
4641 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004642 mFalsingCheck = new Runnable() {
4643 @Override
4644 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004645 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004646 }
4647 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004648 }
4649
4650 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004651 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004652 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004653 if (mCurrMenuRow != null) {
4654 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004655 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004656 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004657 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004658
Mady Mellor95d743c2017-01-10 12:05:27 -08004659 // Slide back any notifications that might be showing a menu
4660 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004661
4662 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004663 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
4664 mCurrMenuRow = row.createMenu();
4665 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4666 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor89e15ec2017-06-28 17:08:21 -07004667 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004668 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004669 }
4670
4671 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004672 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004673 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004674 if (mCurrMenuRow != null) {
4675 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004676 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004677 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004678
Mady Mellor95d743c2017-01-10 12:05:27 -08004679 @Override
4680 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4681 float translation) {
4682 if (mCurrMenuRow != null) {
4683 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004684 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004685 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004686 }
4687
4688 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004689 public void dismissChild(final View view, float velocity,
4690 boolean useAccelerateInterpolator) {
4691 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004692 if (mIsExpanded) {
4693 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4694 // of the panel early.
Rohan Shah524cf7b2018-03-15 14:40:02 -07004695 handleChildViewDismissed(view);
Selim Cinekd1395642016-04-28 12:22:42 -07004696 }
Eliot Courtney47098cb2017-10-18 17:30:30 +09004697 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
4698 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4699 false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004700 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004701 }
4702
4703 @Override
4704 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004705 super.snapChild(animView, targetLeft, velocity);
4706 onDragCancelled(animView);
4707 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004708 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004709 }
4710 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004711
4712 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004713 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4714 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004715 }
4716
Mady Mellorbd707492017-05-10 17:51:25 -07004717 public boolean isFalseGesture(MotionEvent ev) {
4718 return super.isFalseGesture(ev);
4719 }
4720
Mady Mellor95d743c2017-01-10 12:05:27 -08004721 private void handleMenuCoveredOrDismissed() {
4722 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4723 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004724 }
4725 }
4726
Mady Mellor4b80b102016-01-22 08:03:58 -08004727 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004728 public Animator getViewTranslationAnimator(View v, float target,
4729 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004730 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004731 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4732 } else {
4733 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004734 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004735 }
4736
4737 @Override
4738 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004739 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004740 }
4741
4742 @Override
4743 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004744 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004745 }
4746
Mady Mellor95d743c2017-01-10 12:05:27 -08004747 @Override
4748 public void dismiss(View animView, float velocity) {
4749 dismissChild(animView, velocity,
4750 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4751 }
4752
4753 @Override
4754 public void snap(View animView, float targetLeft, float velocity) {
4755 snapChild(animView, targetLeft, velocity);
4756 }
4757
4758 @Override
4759 public boolean swipedFarEnough(float translation, float viewSize) {
4760 return swipedFarEnough();
4761 }
4762
4763 @Override
4764 public boolean swipedFastEnough(float translation, float velocity) {
4765 return swipedFastEnough();
4766 }
4767
4768 @Override
4769 public float getMinDismissVelocity() {
4770 return getEscapeVelocity();
4771 }
4772
4773 public void onMenuShown(View animView) {
4774 onDragCancelled(animView);
4775
4776 // If we're on the lockscreen we want to false this.
4777 if (isAntiFalsingNeeded()) {
4778 mHandler.removeCallbacks(mFalsingCheck);
4779 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4780 }
4781 }
4782
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004783 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09004784 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004785 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004786 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4787 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004788 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004789 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004790 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004791 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004792 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004793 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004794 if (view != null && !isTouchInView(ev, view)) {
4795 // Touch was outside visible guts / menu notification, close what's visible
Eliot Courtney47098cb2017-10-18 17:30:30 +09004796 mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
4797 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4798 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004799 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004800 }
4801 }
4802
Mady Mellor95d743c2017-01-10 12:05:27 -08004803 public void resetExposedMenuView(boolean animate, boolean force) {
4804 if (mMenuExposedView == null
4805 || (!force && mMenuExposedView == mTranslatingParentView)) {
4806 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004807 return;
4808 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004809 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004810 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004811 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004812 0 /* leftTarget */, null /* updateListener */);
4813 if (anim != null) {
4814 anim.start();
4815 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004816 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
Selim Cinekb2e0f332017-08-18 12:24:38 -07004817 ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
4818 if (!row.isRemoved()) {
4819 row.resetTranslation();
4820 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004821 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004822 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004823 }
4824 }
4825
Mady Mellorc2dbe492017-03-30 13:22:03 -07004826 private boolean isTouchInView(MotionEvent ev, View view) {
4827 if (view == null) {
4828 return false;
4829 }
4830 final int height = (view instanceof ExpandableView)
4831 ? ((ExpandableView) view).getActualHeight()
4832 : view.getHeight();
4833 final int rx = (int) ev.getRawX();
4834 final int ry = (int) ev.getRawY();
4835 view.getLocationOnScreen(mTempInt2);
4836 final int x = mTempInt2[0];
4837 final int y = mTempInt2[1];
4838 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4839 boolean ret = rect.contains(rx, ry);
4840 return ret;
4841 }
4842
Selim Cinek33223572016-02-19 19:32:22 -08004843 private void updateContinuousShadowDrawing() {
4844 boolean continuousShadowUpdate = mAnimationRunning
4845 || !mAmbientState.getDraggedViews().isEmpty();
4846 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4847 if (continuousShadowUpdate) {
4848 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4849 } else {
4850 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4851 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004852 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004853 }
4854 }
4855
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004856 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004857 public void resetExposedMenuView(boolean animate, boolean force) {
4858 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004859 }
4860
4861 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4862 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4863 }
4864
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004865 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004866
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004867 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4868
4869 // ANIMATION_TYPE_ADD
4870 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004871 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004872 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004873 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004874 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004875 .animateZ()
4876 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004877
4878 // ANIMATION_TYPE_REMOVE
4879 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004880 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004881 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004882 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004883 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004884 .animateZ()
4885 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004886
4887 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4888 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004889 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004890 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004891 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004892 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004893 .animateZ()
4894 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004895
4896 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4897 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004898 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004899 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004900 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004901 .animateY()
4902 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004903 .animateZ(),
4904
4905 // ANIMATION_TYPE_START_DRAG
4906 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004907 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004908
4909 // ANIMATION_TYPE_SNAP_BACK
4910 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004911 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004912 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004913
4914 // ANIMATION_TYPE_ACTIVATED_CHILD
4915 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004916 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004917
4918 // ANIMATION_TYPE_DIMMED
4919 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004920 .animateDimmed(),
4921
4922 // ANIMATION_TYPE_CHANGE_POSITION
4923 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004924 .animateAlpha() // maybe the children change positions
4925 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004926 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004927 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004928 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004929 .animateZ(),
4930
4931 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004932 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004933
4934 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4935 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004936 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004937 .animateHeight()
4938 .animateTopInset()
4939 .animateY()
4940 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004941 .animateZ()
4942 .hasDelays(),
4943
4944 // ANIMATION_TYPE_HIDE_SENSITIVE
4945 new AnimationFilter()
4946 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02004947
4948 // ANIMATION_TYPE_VIEW_RESIZE
4949 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004950 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02004951 .animateHeight()
4952 .animateTopInset()
4953 .animateY()
4954 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02004955
Selim Cinekb5605e52015-02-20 18:21:41 +01004956 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
4957 new AnimationFilter()
4958 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004959 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01004960 .animateHeight()
4961 .animateTopInset()
4962 .animateY()
4963 .animateZ(),
4964
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004965 // ANIMATION_TYPE_HEADS_UP_APPEAR
4966 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004967 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004968 .animateHeight()
4969 .animateTopInset()
4970 .animateY()
4971 .animateZ(),
4972
4973 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4974 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004975 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004976 .animateHeight()
4977 .animateTopInset()
4978 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07004979 .animateZ()
4980 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004981
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004982 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4983 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004984 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004985 .animateHeight()
4986 .animateTopInset()
4987 .animateY()
4988 .animateZ()
4989 .hasDelays(),
4990
Selim Cineka59ecc32015-04-07 10:51:49 -07004991 // ANIMATION_TYPE_HEADS_UP_OTHER
4992 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004993 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07004994 .animateHeight()
4995 .animateTopInset()
4996 .animateY()
4997 .animateZ(),
4998
Selim Cinekd9acca52014-09-01 22:33:25 +02004999 // ANIMATION_TYPE_EVERYTHING
5000 new AnimationFilter()
5001 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005002 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02005003 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02005004 .animateDimmed()
5005 .animateHideSensitive()
5006 .animateHeight()
5007 .animateTopInset()
5008 .animateY()
5009 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005010 };
5011
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005012 static int[] LENGTHS = new int[] {
5013
5014 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005015 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005016
5017 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005018 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005019
5020 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5021 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5022
5023 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5024 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5025
5026 // ANIMATION_TYPE_START_DRAG
5027 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5028
5029 // ANIMATION_TYPE_SNAP_BACK
5030 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5031
5032 // ANIMATION_TYPE_ACTIVATED_CHILD
5033 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
5034
5035 // ANIMATION_TYPE_DIMMED
5036 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005037
5038 // ANIMATION_TYPE_CHANGE_POSITION
5039 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04005040
5041 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07005042 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005043
5044 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5045 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02005046
5047 // ANIMATION_TYPE_HIDE_SENSITIVE
5048 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02005049
5050 // ANIMATION_TYPE_VIEW_RESIZE
5051 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02005052
Selim Cinekb5605e52015-02-20 18:21:41 +01005053 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08005054 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01005055
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005056 // ANIMATION_TYPE_HEADS_UP_APPEAR
5057 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
5058
5059 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5060 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5061
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005062 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5063 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5064
Selim Cineka59ecc32015-04-07 10:51:49 -07005065 // ANIMATION_TYPE_HEADS_UP_OTHER
5066 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5067
Selim Cinekd9acca52014-09-01 22:33:25 +02005068 // ANIMATION_TYPE_EVERYTHING
5069 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005070 };
5071
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005072 static final int ANIMATION_TYPE_ADD = 0;
5073 static final int ANIMATION_TYPE_REMOVE = 1;
5074 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
5075 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
5076 static final int ANIMATION_TYPE_START_DRAG = 4;
5077 static final int ANIMATION_TYPE_SNAP_BACK = 5;
5078 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
5079 static final int ANIMATION_TYPE_DIMMED = 7;
5080 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04005081 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005082 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02005083 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02005084 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01005085 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005086 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
5087 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005088 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
5089 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
5090 static final int ANIMATION_TYPE_EVERYTHING = 18;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005091
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005092 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
5093 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
5094
Selim Cinek572bbd42014-04-25 16:43:27 +02005095 final long eventStartTime;
5096 final View changingView;
5097 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005098 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005099 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005100 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005101 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07005102 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02005103
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005104 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005105 this(view, type, LENGTHS[type]);
5106 }
5107
Adrian Roos28f90c72017-05-08 17:24:26 -07005108 AnimationEvent(View view, int type, AnimationFilter filter) {
5109 this(view, type, LENGTHS[type], filter);
5110 }
5111
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005112 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07005113 this(view, type, length, FILTERS[type]);
5114 }
5115
5116 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02005117 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
5118 changingView = view;
5119 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005120 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07005121 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005122 }
5123
5124 /**
5125 * Combines the length of several animation events into a single value.
5126 *
5127 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005128 * @return The combined length. Depending on the event types, this might be the maximum of
5129 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005130 */
5131 static long combineLength(ArrayList<AnimationEvent> events) {
5132 long length = 0;
5133 int size = events.size();
5134 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005135 AnimationEvent event = events.get(i);
5136 length = Math.max(length, event.length);
5137 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
5138 return event.length;
5139 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005140 }
5141 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02005142 }
5143 }
Selim Cinek67b22602014-03-10 15:40:16 +01005144}