blob: a0b3c655e99fbf6f09366f110a65ce4eab67f3b8 [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 Dupin3d7ccaf2018-04-02 21:19:23 -070072import com.android.keyguard.KeyguardSliceView;
Lucas Dupine17ce522017-07-17 15:45:06 -070073import com.android.settingslib.Utils;
Rohan Shah524cf7b2018-03-15 14:40:02 -070074import com.android.systemui.Dependency;
Selim Cinek67b22602014-03-10 15:40:16 +010075import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080076import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010077import com.android.systemui.R;
78import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070079import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080080import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
81import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
82import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Selim Cineka32ab602014-06-11 15:06:01 +020083import com.android.systemui.statusbar.ActivatableNotificationView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020084import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010085import com.android.systemui.statusbar.ExpandableNotificationRow;
Jorim Jaggibe565df2014-04-28 17:51:23 +020086import com.android.systemui.statusbar.ExpandableView;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -040087import com.android.systemui.statusbar.FooterView;
Rohan Shah524cf7b2018-03-15 14:40:02 -070088import com.android.systemui.statusbar.NotificationBlockingHelperManager;
Adrian Roos7d062c42017-03-30 15:11:43 -070089import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070090import com.android.systemui.statusbar.NotificationGuts;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090091import com.android.systemui.statusbar.NotificationListContainer;
92import com.android.systemui.statusbar.NotificationLogger;
Selim Cinek0cfbef42016-11-09 19:06:36 -080093import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070094import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010095import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020096import com.android.systemui.statusbar.StatusBarState;
Selim Cinek33223572016-02-19 19:32:22 -080097import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080098import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080099import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Lucas Dupinb561eda2018-04-09 17:25:04 -0700100import com.android.systemui.statusbar.phone.DozeParameters;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900101import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Selim Cinekb5605e52015-02-20 18:21:41 +0100102import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinekaac93252015-04-14 20:04:12 -0700103import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800104import com.android.systemui.statusbar.phone.StatusBar;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900105import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100106import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +0100107
Selim Cinek707e2072017-06-30 18:32:40 +0200108import java.io.FileDescriptor;
109import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200110import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800111import java.util.Collections;
112import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200113import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800114import java.util.List;
Beverlybadf6e42018-04-18 17:32:56 -0400115import java.util.Objects;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800116import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200117
Selim Cinek67b22602014-03-10 15:40:16 +0100118/**
119 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
120 */
121public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200122 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800123 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900124 NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
125 NotificationListContainer {
Selim Cinek67b22602014-03-10 15:40:16 +0100126
Selim Cinekd35c2792016-01-21 13:20:57 -0800127 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800128 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100129 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200130 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
131 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200132 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100133 /**
134 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
135 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800136 private static final int INVALID_POINTER = -1;
Selim Cinek67b22602014-03-10 15:40:16 +0100137
Selim Cinek1408eb52014-06-02 14:45:38 +0200138 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800139 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200140 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100141 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800142 private final Paint mBackgroundPaint = new Paint();
Selim Cinek515b2032017-11-15 10:20:19 -0800143 private final Path mBackgroundPath = new Path();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700144 private final boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100145
Selim Cinekbc243a92016-09-27 16:35:13 -0700146 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100147 private int mOwnScrollY;
148 private int mMaxLayoutHeight;
149
150 private VelocityTracker mVelocityTracker;
151 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100152 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100153 private int mTouchSlop;
154 private int mMinimumVelocity;
155 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100156 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200157 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100158 private boolean mIsBeingDragged;
159 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200160 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700161 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100162 private boolean mTouchIsClick;
163 private float mInitialTouchX;
164 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100165
Selim Cinek67b22602014-03-10 15:40:16 +0100166 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100167 private int mContentHeight;
Lucas Dupin60661a62018-04-12 10:50:13 -0700168 private int mIntrinsicContentHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100169 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100170 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800171 private int mIncreasedPaddingBetweenElements;
Lucas Dupind285cf02018-01-18 09:18:23 -0800172 private int mRegularTopPadding;
173 private int mDarkTopPadding;
174 // Current padding, will be either mRegularTopPadding or mDarkTopPadding
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200175 private int mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800176 // Distance between AOD separator and shelf
177 private int mDarkSeparatorPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700178 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700179 private int mBottomInset = 0;
Selim Cinek67b22602014-03-10 15:40:16 +0100180
181 /**
182 * The algorithm which calculates the properties for our children
183 */
Muyuan Li87798022016-04-07 17:51:25 -0700184 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100185
186 /**
187 * The current State this Layout is in
188 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200189 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700190 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100191 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800192 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700193 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
194 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
195 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
196 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
197 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200198 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700199 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
200 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200201 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200202 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200203 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800204 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200205
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200206 /**
207 * The raw amount of the overScroll on the top, which is not rubber-banded.
208 */
209 private float mOverScrolledTopPixels;
210
211 /**
212 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
213 */
214 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900215 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200216 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200217 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100218 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200219 private boolean mNeedsAnimation;
220 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200221 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200222 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400223 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100224 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200225 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200226 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200227 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200228 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200229 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700230 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200231 private boolean mExpandingNotification;
232 private boolean mExpandedInThisMotion;
Muyuan Li84b45612016-06-01 11:05:08 -0700233 protected boolean mScrollingEnabled;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400234 protected FooterView mFooterView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700235 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400236 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700237 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200238
239 /**
240 * Was the scroller scrolled to the top when the down motion was observed?
241 */
242 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200243 /**
244 * The minimal amount of over scroll which is needed in order to switch to the quick settings
245 * when over scrolling on a expanded card.
246 */
247 private float mMinTopOverScrollToEscape;
248 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700249 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200250 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200251 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700252 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200253 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100254 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700255 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700256
Selim Cinek1408eb52014-06-02 14:45:38 +0200257 /**
258 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
259 * This is needed to avoid scrolling too far after the notification was collapsed in the same
260 * motion.
261 */
262 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000263 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800264
Mady Mellor95d743c2017-01-10 12:05:27 -0800265 private NotificationMenuRowPlugin mCurrMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800266 private View mTranslatingParentView;
Mady Mellor95d743c2017-01-10 12:05:27 -0800267 private View mMenuExposedView;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700268 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200269
270 /**
271 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
272 * animating.
273 */
274 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700275 private boolean mDisallowDismissInThisMotion;
Selim Cineka59ecc32015-04-07 10:51:49 -0700276 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700277 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200278 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200279 = new ViewTreeObserver.OnPreDrawListener() {
280 @Override
281 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400282 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200283 updateChildren();
284 mChildrenUpdateRequested = false;
285 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200286 return true;
287 }
288 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500289 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100290 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100291 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700292 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek0fccc722015-07-29 17:04:36 -0700293 private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700294 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
295 = new HashSet<>();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900296 private HeadsUpManagerPhone mHeadsUpManager;
Selim Cinek29aab962018-02-27 17:05:45 -0800297 private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700298 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700299 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200300 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700301 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700302 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800303 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700304 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800305 = new ViewTreeObserver.OnPreDrawListener() {
306 @Override
307 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700308 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800309 return true;
310 }
311 };
312 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800313 private Rect mStartAnimationRect = new Rect();
314 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800315 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800316 private boolean mAnimateNextBackgroundBottom;
317 private boolean mAnimateNextBackgroundTop;
318 private ObjectAnimator mBottomAnimator = null;
319 private ObjectAnimator mTopAnimator = null;
320 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
321 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800322 private int mBgColor;
323 private float mDimAmount;
324 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800325 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800326 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
327 @Override
328 public void onAnimationEnd(Animator animation) {
329 mDimAnimator = null;
330 }
331 };
332 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
333 = new ValueAnimator.AnimatorUpdateListener() {
334
335 @Override
336 public void onAnimationUpdate(ValueAnimator animation) {
337 setDimAmount((Float) animation.getAnimatedValue());
338 }
339 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700340 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800341 private boolean mContinuousShadowUpdate;
342 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
343 = new ViewTreeObserver.OnPreDrawListener() {
344
345 @Override
346 public boolean onPreDraw() {
347 updateViewShadows();
348 return true;
349 }
350 };
351 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
352 @Override
353 public int compare(ExpandableView view, ExpandableView otherView) {
354 float endY = view.getTranslationY() + view.getActualHeight();
355 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
356 if (endY < otherEndY) {
357 return -1;
358 } else if (endY > otherEndY) {
359 return 1;
360 } else {
361 // The two notifications end at the same location
362 return 0;
363 }
364 }
365 };
Selim Cinek25503252016-03-03 15:31:43 -0800366 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900367 private boolean mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700368 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700369 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800370 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700371 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700372 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400373 private View mForcedScroll;
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -0700374 private View mNeedingPulseAnimation;
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -0800375 private float mDarkAmount = 0f;
Lucas Dupind285cf02018-01-18 09:18:23 -0800376 private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
377 new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
Selim Cinek972123d2016-05-03 14:25:58 -0700378 @Override
379 public void setValue(NotificationStackScrollLayout object, float value) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800380 object.setDarkAmount(value);
Selim Cinek972123d2016-05-03 14:25:58 -0700381 }
382
383 @Override
384 public Float get(NotificationStackScrollLayout object) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800385 return object.getDarkAmount();
Selim Cinek972123d2016-05-03 14:25:58 -0700386 }
387 };
Lucas Dupine17ce522017-07-17 15:45:06 -0700388 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700389 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700390 private boolean mForwardScrollable;
391 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700392 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700393 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800394 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700395 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800396 private boolean mNoAmbient;
397 private final Rect mClipRect = new Rect();
398 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700399 private Rect mRequestedClipBounds;
400 private boolean mInHeadsUpPinnedMode;
401 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800402 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100403 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700404 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800405 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek0fe07392017-11-09 13:26:34 -0800406 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800407 private int mSidePaddings;
Lucas Dupind285cf02018-01-18 09:18:23 -0800408 private final int mSeparatorWidth;
409 private final int mSeparatorThickness;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800410 private final Rect mBackgroundAnimationRect = new Rect();
Lucas Dupin0cd882f2018-01-30 12:19:49 -0800411 private int mAntiBurnInOffsetX;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800412 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
413 private int mHeadsUpInset;
Selim Cinek67b22602014-03-10 15:40:16 +0100414
415 public NotificationStackScrollLayout(Context context) {
416 this(context, null);
417 }
418
419 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
420 this(context, attrs, 0);
421 }
422
423 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
424 this(context, attrs, defStyleAttr, 0);
425 }
426
427 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
428 int defStyleRes) {
429 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800430 Resources res = getResources();
431
Selim Cinek281c2022016-10-13 19:14:43 -0700432 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800433 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800434 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
435 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200436 mExpandHelper = new ExpandHelper(getContext(), this,
437 minHeight, maxHeight);
438 mExpandHelper.setEventSource(this);
439 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800440 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Muyuan Li333a4fc2016-04-16 17:13:46 -0700441 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100442 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800443 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800444 mShouldDrawNotificationBackground =
445 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700446 mFadeNotificationsOnDismiss =
447 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Lucas Dupind285cf02018-01-18 09:18:23 -0800448 mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
449 mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
450 mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
Selim Cinek29aab962018-02-27 17:05:45 -0800451 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
452 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800453 addOnExpandedHeightListener(mRoundnessManager::setExpanded);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800454
Rohan Shah524cf7b2018-03-15 14:40:02 -0700455 // Blocking helper manager wants to know the expanded state, update as well.
456 NotificationBlockingHelperManager blockingHelperManager =
457 Dependency.get(NotificationBlockingHelperManager.class);
458 addOnExpandedHeightListener((height, unused) -> {
459 blockingHelperManager.setNotificationShadeExpanded(height);
460 });
461
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800462 updateWillNotDraw();
Lucas Dupind285cf02018-01-18 09:18:23 -0800463 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100464 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100465 mDebugPaint = new Paint();
466 mDebugPaint.setColor(0xffff0000);
467 mDebugPaint.setStrokeWidth(2);
468 mDebugPaint.setStyle(Paint.Style.STROKE);
469 }
470 }
471
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900472 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800473 public NotificationSwipeActionHelper getSwipeActionHelper() {
474 return mSwipeHelper;
475 }
476
Selim Cinek67b22602014-03-10 15:40:16 +0100477 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800478 public void onMenuClicked(View view, int x, int y, MenuItem item) {
479 if (mLongPressListener == null) {
480 return;
481 }
482 if (view instanceof ExpandableNotificationRow) {
483 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800484 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
485 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800486 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800487 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800488 }
489
490 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800491 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700492 if (mTranslatingParentView != null && row == mTranslatingParentView) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800493 mMenuExposedView = null;
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700494 mTranslatingParentView = null;
495 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000496 }
497
498 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800499 public void onMenuShown(View row) {
500 mMenuExposedView = mTranslatingParentView;
501 if (row instanceof ExpandableNotificationRow) {
502 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
503 ((ExpandableNotificationRow) row).getStatusBarNotification()
504 .getPackageName());
505 }
506 mSwipeHelper.onMenuShown(row);
507 }
508
Selim Cinek67b22602014-03-10 15:40:16 +0100509 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800510 if (mShouldDrawNotificationBackground
511 && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
512 drawBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700513 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800514
Selim Cinek67b22602014-03-10 15:40:16 +0100515 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800516 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100517 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800518 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100519 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200520 y = getHeight() - getEmptyBottomMargin();
521 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100522 }
523 }
524
Lucas Dupind285cf02018-01-18 09:18:23 -0800525 private void drawBackground(Canvas canvas) {
526 final int lockScreenLeft = mSidePaddings;
527 final int lockScreenRight = getWidth() - mSidePaddings;
528 final int lockScreenTop = mCurrentBounds.top;
529 final int lockScreenBottom = mCurrentBounds.bottom;
530 final int darkLeft = getWidth() / 2 - mSeparatorWidth / 2;
531 final int darkRight = darkLeft + mSeparatorWidth;
532 final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
533 final int darkBottom = darkTop + mSeparatorThickness;
534
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800535 if (mAmbientState.hasPulsingNotifications()) {
Lucas Dupin16cfe452018-02-08 13:14:50 -0800536 // No divider, we have a notification icon instead
537 } else if (mAmbientState.isFullyDark()) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800538 // Only draw divider on AOD if we actually have notifications
539 if (mFirstVisibleBackgroundChild != null) {
540 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
541 }
Lucas Dupind285cf02018-01-18 09:18:23 -0800542 } else {
Lucas Dupin60661a62018-04-12 10:50:13 -0700543 float inverseDark = 1 - mDarkAmount;
544 float yProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(inverseDark);
545 float xProgress = Interpolators.FAST_OUT_SLOW_IN
546 .getInterpolation(inverseDark * 2f);
547
Lucas Dupin16cfe452018-02-08 13:14:50 -0800548 mBackgroundAnimationRect.set(
Lucas Dupin60661a62018-04-12 10:50:13 -0700549 (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
550 (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
551 (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
552 (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
Lucas Dupin16cfe452018-02-08 13:14:50 -0800553 if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
554 canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
555 mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
556 mCornerRadius, mCornerRadius, mBackgroundPaint);
557 }
Lucas Dupind285cf02018-01-18 09:18:23 -0800558 }
Lucas Dupin16cfe452018-02-08 13:14:50 -0800559 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800560 }
561
Selim Cinekd35c2792016-01-21 13:20:57 -0800562 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800563 // No need to update the background color if it's not being drawn.
564 if (!mShouldDrawNotificationBackground) {
565 return;
566 }
567
Lucas Dupinb561eda2018-04-09 17:25:04 -0700568 float alpha =
569 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
570 alpha *= 1f - mDarkAmount;
571 // We need to manually blend in the background color.
572 int scrimColor = mScrimController.getBackgroundColor();
573 int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
574
575 // Interpolate between semi-transparent notification panel background color
576 // and white AOD separator.
577 float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(mDarkAmount);
578 int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800579
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100580 if (mCachedBackgroundColor != color) {
581 mCachedBackgroundColor = color;
582 mBackgroundPaint.setColor(color);
583 invalidate();
584 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800585 }
586
Selim Cinek67b22602014-03-10 15:40:16 +0100587 private void initView(Context context) {
588 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100589 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200590 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100591 final ViewConfiguration configuration = ViewConfiguration.get(context);
592 mTouchSlop = configuration.getScaledTouchSlop();
593 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
594 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100595 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700596
597 Resources res = context.getResources();
598 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800599 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700600 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700601 mPaddingBetweenElements = Math.max(1,
602 res.getDimensionPixelSize(R.dimen.notification_divider_height));
603 mIncreasedPaddingBetweenElements =
604 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
605 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200606 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800607 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700608 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800609 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700610 mMinInteractionHeight = res.getDimensionPixelSize(
611 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800612 mCornerRadius = res.getDimensionPixelSize(
613 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800614 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
615 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200616 }
617
Selim Cinek25503252016-03-03 15:31:43 -0800618 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700619 mDrawBackgroundAsSrc = asSrc;
620 updateSrcDrawing();
621 }
622
623 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800624 if (!mShouldDrawNotificationBackground) {
625 return;
626 }
627
Selim Cinek48ff9b42016-11-09 19:31:51 -0800628 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700629 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800630 invalidate();
631 }
632
Selim Cinekaef92ef2014-06-06 18:06:04 +0200633 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700634 notifyHeightChangeListener(view, false /* needsAnimation */);
635 }
636
637 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +0200638 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700639 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200640 }
Selim Cinek67b22602014-03-10 15:40:16 +0100641 }
642
643 @Override
644 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
645 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -0800646
647 int width = MeasureSpec.getSize(widthMeasureSpec);
648 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
649 MeasureSpec.getMode(widthMeasureSpec));
Selim Cinekfa760d42016-05-10 15:50:53 -0400650 // We need to measure all children even the GONE ones, such that the heights are calculated
651 // correctly as they are used to calculate how many we can fit on the screen.
652 final int size = getChildCount();
653 for (int i = 0; i < size; i++) {
Selim Cinekb95fd182017-12-21 13:03:32 -0800654 measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400655 }
Selim Cinek67b22602014-03-10 15:40:16 +0100656 }
657
658 @Override
659 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100660 // we layout all our children centered on the top
661 float centerX = getWidth() / 2.0f;
662 for (int i = 0; i < getChildCount(); i++) {
663 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400664 // We need to layout all children even the GONE ones, such that the heights are
665 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100666 float width = child.getMeasuredWidth();
667 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100668 child.layout((int) (centerX - width / 2.0f),
669 0,
670 (int) (centerX + width / 2.0f),
671 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100672 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200673 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100674 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200675 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200676 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800677 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700678 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100679 }
680
Selim Cinek5bc852a2015-12-21 12:19:09 -0800681 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
682 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200683 mNeedViewResizeAnimation = true;
684 mNeedsAnimation = true;
685 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200686 }
687
Selim Cinekdb167372016-11-17 15:41:17 -0800688 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
689 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800690 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200691 }
692
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900693 @Override
694 public void setChildLocationsChangedListener(
695 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200696 mListener = listener;
697 }
698
Selim Cineka7d4f822016-12-06 14:34:47 -0800699 @Override
700 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
701 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200702 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800703 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200704 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100705 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800706 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100707 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800708 if (row.getVisibility() != View.VISIBLE) {
709 return false;
710 }
711 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200712 }
713
Selim Cinek67b22602014-03-10 15:40:16 +0100714 private void setMaxLayoutHeight(int maxLayoutHeight) {
715 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700716 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200717 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100718 }
719
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200720 private void updateAlgorithmHeightAndPadding() {
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -0700721 mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700722 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700723 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700724 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100725 }
726
Selim Cinekbc243a92016-09-27 16:35:13 -0700727 private void updateAlgorithmLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800728 mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
729 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -0700730 }
731
Selim Cinek67b22602014-03-10 15:40:16 +0100732 /**
733 * Updates the children views according to the stack scroll algorithm. Call this whenever
734 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
735 */
736 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800737 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800738 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
739 ? 0
740 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200741 mAmbientState.setScrollY(mOwnScrollY);
742 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200743 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200744 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100745 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200746 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100747 }
748 }
749
Selim Cinekc383fd02016-10-21 15:31:26 -0700750 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700751 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800752 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -0700753 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
754 updateBackground();
755 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700756 }
757
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800758 private void updateClippingToTopRoundedCorner() {
Selim Cinek2627d722018-01-19 12:16:49 -0800759 Float clipStart = (float) mTopPadding + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800760 Float clipEnd = clipStart + mCornerRadius;
761 boolean first = true;
762 for (int i = 0; i < getChildCount(); i++) {
763 ExpandableView child = (ExpandableView) getChildAt(i);
764 if (child.getVisibility() == GONE) {
765 continue;
766 }
767 float start = child.getTranslationY();
768 float end = start + Math.max(child.getActualHeight() - child.getClipBottomAmount(),
769 0);
770 boolean clip = clipStart > start && clipStart < end
771 || clipEnd >= start && clipEnd <= end;
772 clip &= !(first && mOwnScrollY == 0);
773 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : -1);
774 first = false;
775 }
776 }
777
Selim Cinek3776fe02016-02-04 13:32:43 -0800778 private void updateScrollStateForAddedChildren() {
779 if (mChildrenToAddAnimated.isEmpty()) {
780 return;
781 }
782 for (int i = 0; i < getChildCount(); i++) {
783 ExpandableView child = (ExpandableView) getChildAt(i);
784 if (mChildrenToAddAnimated.contains(child)) {
785 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800786 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
787 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
788 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800789 int childHeight = getIntrinsicHeight(child) + padding;
790 if (startingPosition < mOwnScrollY) {
791 // This child starts off screen, so let's keep it offscreen to keep the others visible
792
Selim Cinekef406062016-09-29 17:33:13 -0700793 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800794 }
795 }
796 }
797 clampScrollPosition();
798 }
799
Adrian Roos181385c2016-05-05 17:45:44 -0400800 private void updateForcedScroll() {
801 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
802 || !mForcedScroll.isAttachedToWindow())) {
803 mForcedScroll = null;
804 }
805 if (mForcedScroll != null) {
806 ExpandableView expandableView = (ExpandableView) mForcedScroll;
807 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
808 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700809 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400810
811 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700812
813 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
814 // that it is not visible anymore.
815 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700816 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400817 }
818 }
819 }
820
Selim Cinek319bdc42014-05-01 23:01:58 +0200821 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200822 if (!mChildrenUpdateRequested) {
823 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
824 mChildrenUpdateRequested = true;
825 invalidate();
826 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200827 }
828
Selim Cinek67b22602014-03-10 15:40:16 +0100829 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200830 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100831 }
832
Selim Cinekf7a14c02014-07-07 14:01:46 +0200833 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100834 int scrollRange = getScrollRange();
835 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700836 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100837 }
838 }
839
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200840 public int getTopPadding() {
841 return mTopPadding;
842 }
843
Selim Cinek1408eb52014-06-02 14:45:38 +0200844 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800845 if (mRegularTopPadding != topPadding) {
846 mRegularTopPadding = topPadding;
847 mDarkTopPadding = topPadding + mDarkSeparatorPadding;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800848 mAmbientState.setDarkTopPadding(mDarkTopPadding);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200849 updateAlgorithmHeightAndPadding();
850 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200851 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200852 mTopPaddingNeedsAnimation = true;
853 mNeedsAnimation = true;
854 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200855 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -0700856 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200857 }
858 }
859
860 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700861 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200862 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700863 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200864 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700865 public void setExpandedHeight(float height) {
866 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700867 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800868 int minExpansionHeight = getMinExpansionHeight();
869 if (height < minExpansionHeight) {
870 mClipRect.left = 0;
871 mClipRect.right = getWidth();
872 mClipRect.top = 0;
873 mClipRect.bottom = (int) height;
874 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700875 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800876 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700877 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800878 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200879 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700880 float translationY;
881 float appearEndPosition = getAppearEndPosition();
882 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800883 float appearFraction = 1.0f;
Selim Cinek94c2d822016-07-13 18:50:04 -0700884 if (height >= appearEndPosition) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700885 translationY = 0;
Selim Cinek94c2d822016-07-13 18:50:04 -0700886 stackHeight = (int) height;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200887 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800888 appearFraction = getAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -0700889 if (appearFraction >= 0) {
890 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
891 appearFraction);
892 } else {
893 // This may happen when pushing up a heads up. We linearly push it up from the
894 // start
895 translationY = height - appearStartPosition + getExpandTranslationStart();
896 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800897 if (isHeadsUpTransition()) {
898 stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
899 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
900 } else {
901 stackHeight = (int) (height - translationY);
902 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200903 }
904 if (stackHeight != mCurrentStackHeight) {
905 mCurrentStackHeight = stackHeight;
906 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200907 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200908 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700909 setStackTranslation(translationY);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800910 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
911 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
912 listener.accept(mExpandedHeight, appearFraction);
913 }
Selim Cinekcafa87f2016-10-26 17:00:17 -0700914 }
915
916 private void setRequestedClipBounds(Rect clipRect) {
917 mRequestedClipBounds = clipRect;
918 updateClipping();
919 }
920
Lucas Dupin60661a62018-04-12 10:50:13 -0700921 /**
922 * Return the height of the content ignoring the footer.
923 */
924 public int getIntrinsicContentHeight() {
925 return mIntrinsicContentHeight;
926 }
927
Selim Cinekcafa87f2016-10-26 17:00:17 -0700928 public void updateClipping() {
Lucas Dupin16cfe452018-02-08 13:14:50 -0800929 boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700930 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
931 && !mHeadsUpAnimatingAway;
932 if (mIsClipped != clipped) {
933 mIsClipped = clipped;
934 updateFadingState();
935 }
Lucas Dupin16cfe452018-02-08 13:14:50 -0800936
937 if (animatingClipping) {
938 setClipBounds(mBackgroundAnimationRect);
939 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700940 setClipBounds(mRequestedClipBounds);
941 } else {
942 setClipBounds(null);
943 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700944 }
945
946 /**
947 * @return The translation at the beginning when expanding.
948 * Measured relative to the resting position.
949 */
950 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800951 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700952 }
953
954 /**
955 * @return the position from where the appear transition starts when expanding.
956 * Measured in absolute height.
957 */
958 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800959 if (isHeadsUpTransition()) {
960 return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -0700961 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800962 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700963 }
964
965 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900966 * @return the height of the top heads up notification when pinned. This is different from the
967 * intrinsic height, which also includes whether the notification is system expanded and
968 * is mainly used when dragging down from a heads up notification.
969 */
970 private int getTopHeadsUpPinnedHeight() {
971 NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
972 if (topEntry == null) {
973 return 0;
974 }
975 ExpandableNotificationRow row = topEntry.row;
976 if (row.isChildInGroup()) {
977 final ExpandableNotificationRow groupSummary
978 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
979 if (groupSummary != null) {
980 row = groupSummary;
981 }
982 }
983 return row.getPinnedHeadsUpHeight();
984 }
985
986 /**
Selim Cinek94c2d822016-07-13 18:50:04 -0700987 * @return the position from where the appear transition ends when expanding.
988 * Measured in absolute height.
989 */
990 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -0700991 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -0700992 int notGoneChildCount = getNotGoneChildCount();
993 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800994 if (isHeadsUpTransition()
Selim Cinekebf42342017-07-13 15:46:10 +0200995 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900996 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +0100997 } else {
998 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800999 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1000 appearPosition += mShelf.getIntrinsicHeight();
1001 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001002 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001003 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001004 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001005 }
1006 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001007 }
1008
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001009 private boolean isHeadsUpTransition() {
1010 return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
1011 && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
1012 }
1013
Selim Cinek94c2d822016-07-13 18:50:04 -07001014 /**
1015 * @param height the height of the panel
1016 * @return the fraction of the appear animation that has been performed
1017 */
1018 public float getAppearFraction(float height) {
1019 float appearEndPosition = getAppearEndPosition();
1020 float appearStartPosition = getAppearStartPosition();
1021 return (height - appearStartPosition)
1022 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001023 }
1024
Selim Cinekd2281152015-04-10 14:37:46 -07001025 public float getStackTranslation() {
1026 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001027 }
1028
Selim Cinekd2281152015-04-10 14:37:46 -07001029 private void setStackTranslation(float stackTranslation) {
1030 if (stackTranslation != mStackTranslation) {
1031 mStackTranslation = stackTranslation;
1032 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001033 requestChildrenUpdate();
1034 }
Selim Cinek67b22602014-03-10 15:40:16 +01001035 }
1036
1037 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001038 * 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 +01001039 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1040 *
1041 * @return either the layout height or the externally defined height, whichever is smaller
1042 */
Selim Cinek343e6e22014-04-11 21:23:30 +02001043 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001044 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1045 }
1046
Selim Cinek816c8e42015-11-19 12:00:45 -08001047 public int getFirstItemMinHeight() {
1048 final ExpandableView firstChild = getFirstChildNotGone();
1049 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001050 }
1051
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001052 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
Dan Sandler4247a5c2014-07-23 15:58:08 -04001053 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +01001054 }
1055
Jason Monk16ac3772016-02-10 15:39:21 -05001056 public void setQsContainer(ViewGroup qsContainer) {
1057 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001058 }
1059
Rohan Shah524cf7b2018-03-15 14:40:02 -07001060 /**
1061 * Handles cleanup after the given {@code view} has been fully swiped out (including
1062 * re-invoking dismiss logic in case the notification has not made its way out yet).
1063 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001064 @Override
Rohan Shah524cf7b2018-03-15 14:40:02 -07001065 public void onChildDismissed(View view) {
1066 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekd1395642016-04-28 12:22:42 -07001067 if (!row.isDismissed()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001068 handleChildViewDismissed(view);
Selim Cinekd1395642016-04-28 12:22:42 -07001069 }
1070 ViewGroup transientContainer = row.getTransientContainer();
1071 if (transientContainer != null) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001072 transientContainer.removeTransientView(view);
Selim Cinekd1395642016-04-28 12:22:42 -07001073 }
1074 }
1075
Rohan Shah524cf7b2018-03-15 14:40:02 -07001076 /**
1077 * Starts up notification dismiss and tells the notification, if any, to remove itself from
1078 * layout.
1079 *
1080 * @param view view (e.g. notification) to dismiss from the layout
1081 */
1082 private void handleChildViewDismissed(View view) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04001083 if (mDismissAllInProgress) {
1084 return;
1085 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001086
1087 boolean isBlockingHelperShown = false;
1088
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001089 setSwipingInProgress(false);
Rohan Shah524cf7b2018-03-15 14:40:02 -07001090 if (mDragAnimPendingChildren.contains(view)) {
1091 // We start the swipe and finish it in the same frame; we don't want a drag animation.
1092 mDragAnimPendingChildren.remove(view);
Selim Cinekeb973562014-05-02 17:07:49 +02001093 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001094 mAmbientState.onDragFinished(view);
Selim Cinek33223572016-02-19 19:32:22 -08001095 updateContinuousShadowDrawing();
Rohan Shah524cf7b2018-03-15 14:40:02 -07001096
1097 if (view instanceof ExpandableNotificationRow) {
1098 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001099 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -07001100 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001101 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001102 isBlockingHelperShown =
1103 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001104 }
Blazej Magnowski72323322015-07-24 11:49:40 -07001105
Rohan Shah524cf7b2018-03-15 14:40:02 -07001106 if (!isBlockingHelperShown) {
1107 mSwipedOutViews.add(view);
1108 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001109 mFalsingManager.onNotificationDismissed();
1110 if (mFalsingManager.shouldEnforceBouncer()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001111 mStatusBar.executeRunnableDismissingKeyguard(
1112 null,
1113 null /* cancelAction */,
1114 false /* dismissShade */,
1115 true /* afterKeyguardGone */,
1116 false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -07001117 }
Selim Cinekeb973562014-05-02 17:07:49 +02001118 }
1119
1120 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08001121 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001122 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -08001123 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +02001124 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001125 if (mAnimationsEnabled) {
1126 mSnappedBackChildren.add(animView);
1127 mNeedsAnimation = true;
1128 }
Selim Cinekeb973562014-05-02 17:07:49 +02001129 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +02001130 } else {
1131 // We start the swipe and snap back in the same frame, we don't want any animation
1132 mDragAnimPendingChildren.remove(animView);
1133 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001134 if (mCurrMenuRow != null && targetLeft == 0) {
1135 mCurrMenuRow.resetMenu();
1136 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08001137 }
Selim Cinek67b22602014-03-10 15:40:16 +01001138 }
1139
Adrian Roos5d9cc662014-05-28 17:08:13 +02001140 @Override
1141 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Anthony Chen7acbb772017-04-07 16:45:25 -07001142 // Returning true prevents alpha fading.
1143 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +02001144 }
1145
Mady Mellorb0a82462016-04-30 17:31:02 -07001146 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001147 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001148 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001149 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001150 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -08001151 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -07001152 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001153 mDragAnimPendingChildren.add(v);
1154 mNeedsAnimation = true;
1155 }
Selim Cinekeb973562014-05-02 17:07:49 +02001156 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +01001157 }
1158
Selim Cinek684a4422015-04-15 16:18:39 -07001159 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001160 if (v instanceof ExpandableNotificationRow) {
1161 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001162 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001163 }
1164 return false;
1165 }
1166
1167 private boolean isHeadsUp(View v) {
1168 if (v instanceof ExpandableNotificationRow) {
1169 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1170 return row.isHeadsUp();
1171 }
1172 return false;
1173 }
1174
Mady Mellorb0a82462016-04-30 17:31:02 -07001175 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001176 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001177 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001178 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001179 }
1180
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001181 @Override
1182 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001183 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001184 }
1185
Mady Mellor4b80b102016-01-22 08:03:58 -08001186 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001187 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001188 View child = getChildAtPosition(ev.getX(), ev.getY());
1189 if (child instanceof ExpandableNotificationRow) {
1190 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1191 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001192 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001193 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001194 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001195 || (parent.getNotificationChildren().size() == 1
1196 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001197 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001198 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001199 // child notifications so we use the parent of the child. We also do the same
1200 // if we only have a single child.
1201 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001202 }
1203 }
1204 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001205 }
1206
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001207 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1208 getLocationOnScreen(mTempInt2);
1209 float localTouchY = touchY - mTempInt2[1];
1210
1211 ExpandableView closestChild = null;
1212 float minDist = Float.MAX_VALUE;
1213
1214 // find the view closest to the location, accounting for GONE views
1215 final int count = getChildCount();
1216 for (int childIdx = 0; childIdx < count; childIdx++) {
1217 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1218 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001219 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001220 continue;
1221 }
1222 float childTop = slidingChild.getTranslationY();
1223 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001224 float bottom = childTop + slidingChild.getActualHeight()
1225 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001226
1227 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1228 if (dist < minDist) {
1229 closestChild = slidingChild;
1230 minDist = dist;
1231 }
1232 }
1233 return closestChild;
1234 }
1235
Mady Mellorb0a82462016-04-30 17:31:02 -07001236 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001237 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001238 getLocationOnScreen(mTempInt2);
1239 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001240 }
1241
Mady Mellorb0a82462016-04-30 17:31:02 -07001242 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001243 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001244 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1245
1246 }
1247
1248 /**
1249 * Get the child at a certain screen location.
1250 *
1251 * @param touchX the x coordinate
1252 * @param touchY the y coordinate
1253 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1254 * @return the child at the given location.
1255 */
1256 private ExpandableView getChildAtPosition(float touchX, float touchY,
1257 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001258 // find the view under the pointer, accounting for GONE views
1259 final int count = getChildCount();
1260 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001261 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001262 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001263 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001264 continue;
1265 }
Selim Cinek89faff12014-06-19 16:29:04 -07001266 float childTop = slidingChild.getTranslationY();
1267 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001268 float bottom = childTop + slidingChild.getActualHeight()
1269 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001270
1271 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1272 // camera affordance).
1273 int left = 0;
1274 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001275
Selim Cinek34ed7c02017-09-08 15:03:12 -07001276 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001277 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001278 if (slidingChild instanceof ExpandableNotificationRow) {
1279 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001280 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001281 && mHeadsUpManager.getTopEntry().row != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001282 && mGroupManager.getGroupSummary(
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001283 mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
Selim Cinek5bc852a2015-12-21 12:19:09 -08001284 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001285 continue;
1286 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001287 return row.getViewAtPosition(touchY - childTop);
1288 }
Selim Cinek67b22602014-03-10 15:40:16 +01001289 return slidingChild;
1290 }
1291 }
1292 return null;
1293 }
1294
Mady Mellorb0a82462016-04-30 17:31:02 -07001295 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001296 public boolean canChildBeExpanded(View v) {
1297 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001298 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001299 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001300 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001301 }
1302
Chris Wren6abeeb92016-05-26 14:44:38 -04001303 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001304 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001305 public void setUserExpandedChild(View v, boolean userExpanded) {
1306 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001307 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001308 if (userExpanded && onKeyguard()) {
1309 // Due to a race when locking the screen while touching, a notification may be
1310 // expanded even after we went back to keyguard. An example of this happens if
1311 // you click in the empty space while expanding a group.
1312
1313 // We also need to un-user lock it here, since otherwise the content height
1314 // calculated might be wrong. We also can't invert the two calls since
1315 // un-userlocking it will trigger a layout switch in the content view.
1316 row.setUserLocked(false);
1317 updateContentHeight();
1318 notifyHeightChangeListener(row);
1319 return;
1320 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001321 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1322 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001323 }
1324 }
1325
Mady Mellorb0a82462016-04-30 17:31:02 -07001326 @Override
1327 public void setExpansionCancelled(View v) {
1328 if (v instanceof ExpandableNotificationRow) {
1329 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1330 }
1331 }
1332
1333 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001334 public void setUserLockedChild(View v, boolean userLocked) {
1335 if (v instanceof ExpandableNotificationRow) {
1336 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1337 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001338 cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02001339 requestDisallowInterceptTouchEvent(true);
1340 }
1341
1342 @Override
1343 public void expansionStateChanged(boolean isExpanding) {
1344 mExpandingNotification = isExpanding;
1345 if (!mExpandedInThisMotion) {
1346 mMaxScrollAfterExpand = mOwnScrollY;
1347 mExpandedInThisMotion = true;
1348 }
1349 }
1350
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001351 @Override
1352 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001353 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001354 }
1355
Selim Cinek1408eb52014-06-02 14:45:38 +02001356 public void setScrollingEnabled(boolean enable) {
1357 mScrollingEnabled = enable;
1358 }
1359
Adrian Roos181385c2016-05-05 17:45:44 -04001360 public void lockScrollTo(View v) {
1361 if (mForcedScroll == v) {
1362 return;
1363 }
1364 mForcedScroll = v;
1365 scrollTo(v);
1366 }
1367
Ricky Waicd35def2016-05-03 11:07:07 +01001368 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001369 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001370 int positionInLinearLayout = getPositionInLinearLayout(v);
1371 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1372 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001373
Adrian Roos4a579672016-05-24 16:54:37 -07001374 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1375 // that it is not visible anymore.
1376 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001377 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1378 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001379 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001380 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001381 }
Ricky Waicd35def2016-05-03 11:07:07 +01001382 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001383 }
1384
Adrian Roos181385c2016-05-05 17:45:44 -04001385 /**
1386 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1387 * the IME.
1388 */
1389 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1390 return positionInLinearLayout + v.getIntrinsicHeight() +
1391 getImeInset() - getHeight() + getTopPadding();
1392 }
1393
Adrian Roos5153d4a2016-03-22 10:01:56 -07001394 @Override
1395 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001396 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001397
1398 int range = getScrollRange();
1399 if (mOwnScrollY > range) {
1400 // HACK: We're repeatedly getting staggered insets here while the IME is
1401 // animating away. To work around that we'll wait until things have settled.
1402 removeCallbacks(mReclamp);
1403 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001404 } else if (mForcedScroll != null) {
1405 // The scroll was requested before we got the actual inset - in case we need
1406 // to scroll up some more do so now.
1407 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001408 }
1409 return insets;
1410 }
1411
1412 private Runnable mReclamp = new Runnable() {
1413 @Override
1414 public void run() {
1415 int range = getScrollRange();
1416 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1417 mDontReportNextOverScroll = true;
1418 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001419 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001420 }
1421 };
1422
Selim Cinek1408eb52014-06-02 14:45:38 +02001423 public void setExpandingEnabled(boolean enable) {
1424 mExpandHelper.setEnabled(enable);
1425 }
1426
1427 private boolean isScrollingEnabled() {
1428 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001429 }
1430
Mady Mellorb0a82462016-04-30 17:31:02 -07001431 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001432 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001433 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001434 }
1435
Selim Cinek19c8c702014-08-25 22:09:19 +02001436 @Override
1437 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001438 return onKeyguard();
1439 }
1440
1441 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001442 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001443 }
1444
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001445 private void setSwipingInProgress(boolean isSwiped) {
1446 mSwipingInProgress = isSwiped;
1447 if(isSwiped) {
1448 requestDisallowInterceptTouchEvent(true);
1449 }
Selim Cinek67b22602014-03-10 15:40:16 +01001450 }
1451
1452 @Override
1453 protected void onConfigurationChanged(Configuration newConfig) {
1454 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001455 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001456 float densityScale = getResources().getDisplayMetrics().density;
1457 mSwipeHelper.setDensityScale(densityScale);
1458 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1459 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1460 initView(getContext());
1461 }
1462
Dan Sandlereceda3d2014-07-21 15:35:01 -04001463 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001464 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1465 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001466 }
1467
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001468 @Override
Mady Mellor86889c22016-04-18 16:37:06 -07001469 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001470 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001471 // If the child is showing the notification menu snap to that
1472 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001473 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001474 }
1475
Selim Cinek67b22602014-03-10 15:40:16 +01001476 @Override
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001477 public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
1478 return this;
1479 }
1480
1481 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001482 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001483 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1484 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001485 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001486 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001487 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001488 if (isCancelOrUp) {
1489 mExpandHelper.onlyObserveMovements(false);
1490 }
1491 boolean wasExpandingBefore = mExpandingNotification;
1492 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001493 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1494 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001495 dispatchDownEventToScroller(ev);
1496 }
1497 }
Selim Cinek67b22602014-03-10 15:40:16 +01001498 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001499 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1500 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001501 scrollerWantsIt = onScrollTouch(ev);
1502 }
1503 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001504 if (!mIsBeingDragged
1505 && !mExpandingNotification
1506 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001507 && !mOnlyScrollingInThisMotion
1508 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001509 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1510 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001511
1512 // Check if we need to clear any snooze leavebehinds
Eliot Courtney47098cb2017-10-18 17:30:30 +09001513 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07001514 if (guts != null && !isTouchInView(ev, guts)
1515 && guts.getGutsContent() instanceof NotificationSnooze) {
1516 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1517 if ((ns.isExpanded() && isCancelOrUp)
1518 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1519 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1520 // clear it on the next non-horizontal swipe or expand event.
1521 checkSnoozeLeavebehind();
1522 }
1523 }
1524 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1525 mCheckForLeavebehind = true;
1526 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001527 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1528 }
1529
1530 private void dispatchDownEventToScroller(MotionEvent ev) {
1531 MotionEvent downEvent = MotionEvent.obtain(ev);
1532 downEvent.setAction(MotionEvent.ACTION_DOWN);
1533 onScrollTouch(downEvent);
1534 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001535 }
1536
Selim Cinek11e33232016-08-05 15:30:53 -07001537 @Override
1538 public boolean onGenericMotionEvent(MotionEvent event) {
1539 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1540 || mDisallowScrollingInThisMotion) {
1541 return false;
1542 }
1543 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1544 switch (event.getAction()) {
1545 case MotionEvent.ACTION_SCROLL: {
1546 if (!mIsBeingDragged) {
1547 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1548 if (vscroll != 0) {
1549 final int delta = (int) (vscroll * getVerticalScrollFactor());
1550 final int range = getScrollRange();
1551 int oldScrollY = mOwnScrollY;
1552 int newScrollY = oldScrollY - delta;
1553 if (newScrollY < 0) {
1554 newScrollY = 0;
1555 } else if (newScrollY > range) {
1556 newScrollY = range;
1557 }
1558 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001559 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001560 return true;
1561 }
1562 }
1563 }
1564 }
1565 }
1566 }
1567 return super.onGenericMotionEvent(event);
1568 }
1569
Selim Cinek67b22602014-03-10 15:40:16 +01001570 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001571 if (!isScrollingEnabled()) {
1572 return false;
1573 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001574 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001575 return false;
1576 }
Adrian Roos181385c2016-05-05 17:45:44 -04001577 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001578 initVelocityTrackerIfNotExists();
1579 mVelocityTracker.addMovement(ev);
1580
1581 final int action = ev.getAction();
1582
1583 switch (action & MotionEvent.ACTION_MASK) {
1584 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001585 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001586 return false;
1587 }
1588 boolean isBeingDragged = !mScroller.isFinished();
1589 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001590 /*
1591 * If being flinged and user touches, stop the fling. isFinished
1592 * will be false if being flinged.
1593 */
1594 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001595 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001596 }
1597
1598 // Remember where the motion event started
1599 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001600 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001601 mActivePointerId = ev.getPointerId(0);
1602 break;
1603 }
1604 case MotionEvent.ACTION_MOVE:
1605 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1606 if (activePointerIndex == -1) {
1607 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1608 break;
1609 }
1610
1611 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001612 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001613 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001614 final int xDiff = Math.abs(x - mDownX);
1615 final int yDiff = Math.abs(deltaY);
1616 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001617 setIsBeingDragged(true);
1618 if (deltaY > 0) {
1619 deltaY -= mTouchSlop;
1620 } else {
1621 deltaY += mTouchSlop;
1622 }
1623 }
1624 if (mIsBeingDragged) {
1625 // Scroll to follow the motion event
1626 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001627 int range = getScrollRange();
1628 if (mExpandedInThisMotion) {
1629 range = Math.min(range, mMaxScrollAfterExpand);
1630 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001631
1632 float scrollAmount;
1633 if (deltaY < 0) {
1634 scrollAmount = overScrollDown(deltaY);
1635 } else {
1636 scrollAmount = overScrollUp(deltaY, range);
1637 }
Selim Cinek67b22602014-03-10 15:40:16 +01001638
Selim Cinek9212de82017-02-06 16:04:28 -08001639 // Calling customOverScrollBy will call onCustomOverScrolled, which
1640 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001641 if (scrollAmount != 0.0f) {
1642 // The scrolling motion could not be compensated with the
1643 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001644 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1645 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001646 // If we're scrolling, leavebehinds should be dismissed
1647 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001648 }
Selim Cinek67b22602014-03-10 15:40:16 +01001649 }
1650 break;
1651 case MotionEvent.ACTION_UP:
1652 if (mIsBeingDragged) {
1653 final VelocityTracker velocityTracker = mVelocityTracker;
1654 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1655 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1656
Selim Cinek1408eb52014-06-02 14:45:38 +02001657 if (shouldOverScrollFling(initialVelocity)) {
1658 onOverScrollFling(true, initialVelocity);
1659 } else {
1660 if (getChildCount() > 0) {
1661 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1662 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1663 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1664 fling(-initialVelocity);
1665 } else {
1666 onOverScrollFling(false, initialVelocity);
1667 }
1668 } else {
1669 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1670 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001671 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001672 }
Selim Cinek67b22602014-03-10 15:40:16 +01001673 }
1674 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001675 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001676 mActivePointerId = INVALID_POINTER;
1677 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001678 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001679
Selim Cinek67b22602014-03-10 15:40:16 +01001680 break;
1681 case MotionEvent.ACTION_CANCEL:
1682 if (mIsBeingDragged && getChildCount() > 0) {
1683 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001684 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001685 }
1686 mActivePointerId = INVALID_POINTER;
1687 endDrag();
1688 }
1689 break;
1690 case MotionEvent.ACTION_POINTER_DOWN: {
1691 final int index = ev.getActionIndex();
1692 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001693 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001694 mActivePointerId = ev.getPointerId(index);
1695 break;
1696 }
1697 case MotionEvent.ACTION_POINTER_UP:
1698 onSecondaryPointerUp(ev);
1699 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001700 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001701 break;
1702 }
1703 return true;
1704 }
1705
Muyuan Li84b45612016-06-01 11:05:08 -07001706 protected boolean isInsideQsContainer(MotionEvent ev) {
1707 return ev.getY() < mQsContainer.getBottom();
1708 }
1709
Selim Cinek1408eb52014-06-02 14:45:38 +02001710 private void onOverScrollFling(boolean open, int initialVelocity) {
1711 if (mOverscrollTopChangedListener != null) {
1712 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1713 }
1714 mDontReportNextOverScroll = true;
1715 setOverScrollAmount(0.0f, true, false);
1716 }
1717
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001718 /**
1719 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1720 *
1721 * @param deltaY The amount to scroll upwards, has to be positive.
1722 * @return The amount of scrolling to be performed by the scroller,
1723 * not handled by the overScroll amount.
1724 */
1725 private float overScrollUp(int deltaY, int range) {
1726 deltaY = Math.max(deltaY, 0);
1727 float currentTopAmount = getCurrentOverScrollAmount(true);
1728 float newTopAmount = currentTopAmount - deltaY;
1729 if (currentTopAmount > 0) {
1730 setOverScrollAmount(newTopAmount, true /* onTop */,
1731 false /* animate */);
1732 }
1733 // Top overScroll might not grab all scrolling motion,
1734 // we have to scroll as well.
1735 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1736 float newScrollY = mOwnScrollY + scrollAmount;
1737 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001738 if (!mExpandedInThisMotion) {
1739 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1740 // We overScroll on the top
1741 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1742 false /* onTop */,
1743 false /* animate */);
1744 }
Selim Cinekef406062016-09-29 17:33:13 -07001745 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001746 scrollAmount = 0.0f;
1747 }
1748 return scrollAmount;
1749 }
1750
1751 /**
1752 * Perform a scroll downward and adapt the overscroll amounts accordingly
1753 *
1754 * @param deltaY The amount to scroll downwards, has to be negative.
1755 * @return The amount of scrolling to be performed by the scroller,
1756 * not handled by the overScroll amount.
1757 */
1758 private float overScrollDown(int deltaY) {
1759 deltaY = Math.min(deltaY, 0);
1760 float currentBottomAmount = getCurrentOverScrollAmount(false);
1761 float newBottomAmount = currentBottomAmount + deltaY;
1762 if (currentBottomAmount > 0) {
1763 setOverScrollAmount(newBottomAmount, false /* onTop */,
1764 false /* animate */);
1765 }
1766 // Bottom overScroll might not grab all scrolling motion,
1767 // we have to scroll as well.
1768 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1769 float newScrollY = mOwnScrollY + scrollAmount;
1770 if (newScrollY < 0) {
1771 float currentTopPixels = getCurrentOverScrolledPixels(true);
1772 // We overScroll on the top
1773 setOverScrolledPixels(currentTopPixels - newScrollY,
1774 true /* onTop */,
1775 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001776 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001777 scrollAmount = 0.0f;
1778 }
1779 return scrollAmount;
1780 }
1781
Selim Cinek67b22602014-03-10 15:40:16 +01001782 private void onSecondaryPointerUp(MotionEvent ev) {
1783 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1784 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1785 final int pointerId = ev.getPointerId(pointerIndex);
1786 if (pointerId == mActivePointerId) {
1787 // This was our active pointer going up. Choose a new
1788 // active pointer and adjust accordingly.
1789 // TODO: Make this decision more intelligent.
1790 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1791 mLastMotionY = (int) ev.getY(newPointerIndex);
1792 mActivePointerId = ev.getPointerId(newPointerIndex);
1793 if (mVelocityTracker != null) {
1794 mVelocityTracker.clear();
1795 }
1796 }
1797 }
1798
1799 private void initVelocityTrackerIfNotExists() {
1800 if (mVelocityTracker == null) {
1801 mVelocityTracker = VelocityTracker.obtain();
1802 }
1803 }
1804
1805 private void recycleVelocityTracker() {
1806 if (mVelocityTracker != null) {
1807 mVelocityTracker.recycle();
1808 mVelocityTracker = null;
1809 }
1810 }
1811
1812 private void initOrResetVelocityTracker() {
1813 if (mVelocityTracker == null) {
1814 mVelocityTracker = VelocityTracker.obtain();
1815 } else {
1816 mVelocityTracker.clear();
1817 }
1818 }
1819
Ricky Waicd35def2016-05-03 11:07:07 +01001820 public void setFinishScrollingCallback(Runnable runnable) {
1821 mFinishScrollingCallback = runnable;
1822 }
1823
Selim Cinek9212de82017-02-06 16:04:28 -08001824 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001825 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001826 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001827 int y = mScroller.getCurrY();
1828
Selim Cinek9212de82017-02-06 16:04:28 -08001829 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001830 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001831 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1832 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001833 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001834 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1835 }
1836 }
Selim Cinek67b22602014-03-10 15:40:16 +01001837
Adrian Roos5153d4a2016-03-22 10:01:56 -07001838 if (mDontClampNextScroll) {
1839 range = Math.max(range, oldY);
1840 }
Selim Cinek9212de82017-02-06 16:04:28 -08001841 customOverScrollBy(y - oldY, oldY, range,
1842 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001843 }
1844
Selim Cinek9212de82017-02-06 16:04:28 -08001845 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001846 } else {
1847 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001848 if (mFinishScrollingCallback != null) {
1849 mFinishScrollingCallback.run();
1850 }
Selim Cinek67b22602014-03-10 15:40:16 +01001851 }
1852 }
1853
Selim Cinek9212de82017-02-06 16:04:28 -08001854 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1855 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001856
1857 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001858 final int top = -maxOverScrollY;
1859 final int bottom = maxOverScrollY + scrollRangeY;
1860
1861 boolean clampedY = false;
1862 if (newScrollY > bottom) {
1863 newScrollY = bottom;
1864 clampedY = true;
1865 } else if (newScrollY < top) {
1866 newScrollY = top;
1867 clampedY = true;
1868 }
1869
Selim Cinek9212de82017-02-06 16:04:28 -08001870 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001871
1872 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001873 }
1874
1875 /**
1876 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1877 * overscroll effect based on numPixels. By default this will also cancel animations on the
1878 * same overScroll edge.
1879 *
1880 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1881 * the rubber-banding logic.
1882 * @param onTop Should the effect be applied on top of the scroller.
1883 * @param animate Should an animation be performed.
1884 */
1885 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001886 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001887 }
1888
1889 /**
1890 * Set the effective overScroll amount which will be directly reflected in the layout.
1891 * By default this will also cancel animations on the same overScroll edge.
1892 *
1893 * @param amount The amount to overScroll by.
1894 * @param onTop Should the effect be applied on top of the scroller.
1895 * @param animate Should an animation be performed.
1896 */
1897 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1898 setOverScrollAmount(amount, onTop, animate, true);
1899 }
1900
1901 /**
1902 * Set the effective overScroll amount which will be directly reflected in the layout.
1903 *
1904 * @param amount The amount to overScroll by.
1905 * @param onTop Should the effect be applied on top of the scroller.
1906 * @param animate Should an animation be performed.
1907 * @param cancelAnimators Should running animations be cancelled.
1908 */
1909 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1910 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001911 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1912 }
1913
1914 /**
1915 * Set the effective overScroll amount which will be directly reflected in the layout.
1916 *
1917 * @param amount The amount to overScroll by.
1918 * @param onTop Should the effect be applied on top of the scroller.
1919 * @param animate Should an animation be performed.
1920 * @param cancelAnimators Should running animations be cancelled.
1921 * @param isRubberbanded The value which will be passed to
1922 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1923 */
1924 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1925 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001926 if (cancelAnimators) {
1927 mStateAnimator.cancelOverScrollAnimators(onTop);
1928 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001929 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001930 }
1931
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001932 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1933 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001934 amount = Math.max(0, amount);
1935 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001936 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001937 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001938 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001939 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001940 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001941 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001942 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001943 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001944 }
1945 }
1946
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001947 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001948 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1949 if (mDontReportNextOverScroll) {
1950 mDontReportNextOverScroll = false;
1951 return;
1952 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001953 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001954 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001955 }
1956 }
1957
1958 public void setOverscrollTopChangedListener(
1959 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1960 mOverscrollTopChangedListener = overscrollTopChangedListener;
1961 }
1962
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001963 public float getCurrentOverScrollAmount(boolean top) {
1964 return mAmbientState.getOverScrollAmount(top);
1965 }
1966
1967 public float getCurrentOverScrolledPixels(boolean top) {
1968 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
1969 }
1970
1971 private void setOverScrolledPixels(float amount, boolean onTop) {
1972 if (onTop) {
1973 mOverScrolledTopPixels = amount;
1974 } else {
1975 mOverScrolledBottomPixels = amount;
1976 }
1977 }
1978
Selim Cinek9212de82017-02-06 16:04:28 -08001979 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001980 // Treat animating scrolls differently; see #computeScroll() for why.
1981 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07001982 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001983 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001984 springBack();
1985 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02001986 float overScrollTop = getCurrentOverScrollAmount(true);
1987 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001988 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001989 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001990 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001991 }
Selim Cinek67b22602014-03-10 15:40:16 +01001992 }
Selim Cinek67b22602014-03-10 15:40:16 +01001993 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08001994 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001995 }
1996 }
1997
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001998 private void springBack() {
1999 int scrollRange = getScrollRange();
2000 boolean overScrolledTop = mOwnScrollY <= 0;
2001 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2002 if (overScrolledTop || overScrolledBottom) {
2003 boolean onTop;
2004 float newAmount;
2005 if (overScrolledTop) {
2006 onTop = true;
2007 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07002008 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02002009 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002010 } else {
2011 onTop = false;
2012 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07002013 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002014 }
2015 setOverScrollAmount(newAmount, onTop, false);
2016 setOverScrollAmount(0.0f, onTop, true);
2017 mScroller.forceFinished(true);
2018 }
2019 }
2020
Selim Cinek67b22602014-03-10 15:40:16 +01002021 private int getScrollRange() {
Selim Cineka424c502016-04-05 13:07:54 -07002022 int contentHeight = getContentHeight();
Selim Cinekdb167372016-11-17 15:41:17 -08002023 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002024 int imeInset = getImeInset();
2025 scrollRange += Math.min(imeInset, Math.max(0,
2026 getContentHeight() - (getHeight() - imeInset)));
2027 return scrollRange;
2028 }
2029
2030 private int getImeInset() {
2031 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002032 }
2033
Selim Cinek343e6e22014-04-11 21:23:30 +02002034 /**
2035 * @return the first child which has visibility unequal to GONE
2036 */
Selim Cinekb55386d2015-12-16 17:26:49 -08002037 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002038 int childCount = getChildCount();
2039 for (int i = 0; i < childCount; i++) {
2040 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002041 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002042 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002043 }
2044 }
2045 return null;
2046 }
2047
Selim Cinek4a1ac842014-05-01 15:51:58 +02002048 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002049 * @return the child before the given view which has visibility unequal to GONE
2050 */
2051 public ExpandableView getViewBeforeView(ExpandableView view) {
2052 ExpandableView previousView = null;
2053 int childCount = getChildCount();
2054 for (int i = 0; i < childCount; i++) {
2055 View child = getChildAt(i);
2056 if (child == view) {
2057 return previousView;
2058 }
2059 if (child.getVisibility() != View.GONE) {
2060 previousView = (ExpandableView) child;
2061 }
2062 }
2063 return null;
2064 }
2065
2066 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002067 * @return The first child which has visibility unequal to GONE which is currently below the
2068 * given translationY or equal to it.
2069 */
Selim Cinekef8c2252017-02-10 14:52:18 -08002070 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002071 int childCount = getChildCount();
2072 for (int i = 0; i < childCount; i++) {
2073 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002074 if (child.getVisibility() == View.GONE) {
2075 continue;
2076 }
2077 float rowTranslation = child.getTranslationY();
2078 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002079 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002080 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2081 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2082 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2083 List<ExpandableNotificationRow> notificationChildren =
2084 row.getNotificationChildren();
2085 for (int childIndex = 0; childIndex < notificationChildren.size();
2086 childIndex++) {
2087 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2088 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2089 return rowChild;
2090 }
2091 }
2092 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002093 }
2094 }
2095 return null;
2096 }
2097
2098 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002099 * @return the last child which has visibility unequal to GONE
2100 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002101 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002102 int childCount = getChildCount();
2103 for (int i = childCount - 1; i >= 0; i--) {
2104 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002105 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002106 return child;
2107 }
2108 }
2109 return null;
2110 }
2111
Jorim Jaggi069cd032014-05-15 03:09:01 +02002112 /**
2113 * @return the number of children which have visibility unequal to GONE
2114 */
2115 public int getNotGoneChildCount() {
2116 int childCount = getChildCount();
2117 int count = 0;
2118 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002119 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002120 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002121 count++;
2122 }
2123 }
2124 return count;
2125 }
2126
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002127 public int getContentHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01002128 return mContentHeight;
2129 }
2130
2131 private void updateContentHeight() {
2132 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002133 float previousPaddingRequest = mPaddingBetweenElements;
2134 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002135 int numShownItems = 0;
2136 boolean finish = false;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002137 int maxDisplayedNotifications = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002138 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08002139 : mMaxDisplayedNotifications;
2140
Selim Cinek67b22602014-03-10 15:40:16 +01002141 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002142 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002143 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002144 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002145 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002146 boolean limitReached = maxDisplayedNotifications != -1
2147 && numShownItems >= maxDisplayedNotifications;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002148 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002149 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07002150 && expandableView instanceof ExpandableNotificationRow
2151 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
2152 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002153 expandableView = mShelf;
2154 finish = true;
2155 }
Selim Cinek42357e02016-02-24 18:48:01 -08002156 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002157 float padding;
2158 if (increasedPaddingAmount >= 0.0f) {
2159 padding = (int) NotificationUtils.interpolate(
2160 previousPaddingRequest,
2161 mIncreasedPaddingBetweenElements,
2162 increasedPaddingAmount);
2163 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002164 mPaddingBetweenElements,
2165 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002166 increasedPaddingAmount);
2167 } else {
2168 int ownPadding = (int) NotificationUtils.interpolate(
2169 0,
2170 mPaddingBetweenElements,
2171 1.0f + increasedPaddingAmount);
2172 if (previousPaddingAmount > 0.0f) {
2173 padding = (int) NotificationUtils.interpolate(
2174 ownPadding,
2175 mIncreasedPaddingBetweenElements,
2176 previousPaddingAmount);
2177 } else {
2178 padding = ownPadding;
2179 }
2180 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002181 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002182 if (height != 0) {
2183 height += padding;
2184 }
2185 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002186 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002187 numShownItems++;
2188 if (finish) {
2189 break;
2190 }
Selim Cinek67b22602014-03-10 15:40:16 +01002191 }
2192 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002193 mIntrinsicContentHeight = height;
Anthony Chen9fe1ee72017-04-07 13:53:37 -07002194 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002195 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002196 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002197 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002198 }
2199
Adrian Roos7d062c42017-03-30 15:11:43 -07002200 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002201 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002202 }
2203
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002204 @Override
Selim Cinekbe2c4432017-05-30 12:11:09 -07002205 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002206 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002207 }
2208
Selim Cinekc22fff62016-05-20 12:44:30 -07002209 private void updateScrollability() {
2210 boolean scrollable = getScrollRange() > 0;
2211 if (scrollable != mScrollable) {
2212 mScrollable = scrollable;
2213 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002214 updateForwardAndBackwardScrollability();
2215 }
2216 }
2217
2218 private void updateForwardAndBackwardScrollability() {
2219 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2220 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2221 boolean changed = forwardScrollable != mForwardScrollable
2222 || backwardsScrollable != mBackwardScrollable;
2223 mForwardScrollable = forwardScrollable;
2224 mBackwardScrollable = backwardsScrollable;
2225 if (changed) {
2226 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002227 }
Selim Cinek67b22602014-03-10 15:40:16 +01002228 }
2229
Selim Cinek6811d722016-01-19 17:53:12 -08002230 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002231 // No need to update the background color if it's not being drawn.
Lucas Dupin16cfe452018-02-08 13:14:50 -08002232 if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002233 return;
2234 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002235
Selim Cinek6811d722016-01-19 17:53:12 -08002236 updateBackgroundBounds();
2237 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002238 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2239 || areBoundsAnimating();
2240 if (!isExpanded()) {
2241 abortBackgroundAnimators();
2242 animate = false;
2243 }
2244 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002245 startBackgroundAnimation();
2246 } else {
2247 mCurrentBounds.set(mBackgroundBounds);
2248 applyCurrentBackgroundBounds();
2249 }
2250 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002251 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002252 }
Selim Cinek614576e2016-01-20 10:54:09 -08002253 mAnimateNextBackgroundBottom = false;
2254 mAnimateNextBackgroundTop = false;
2255 }
2256
Selim Cinek54680902016-10-19 16:49:44 -07002257 private void abortBackgroundAnimators() {
2258 if (mBottomAnimator != null) {
2259 mBottomAnimator.cancel();
2260 }
2261 if (mTopAnimator != null) {
2262 mTopAnimator.cancel();
2263 }
2264 }
2265
Selim Cinek614576e2016-01-20 10:54:09 -08002266 private boolean areBoundsAnimating() {
2267 return mBottomAnimator != null || mTopAnimator != null;
2268 }
2269
2270 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002271 // left and right are always instantly applied
2272 mCurrentBounds.left = mBackgroundBounds.left;
2273 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002274 startBottomAnimation();
2275 startTopAnimation();
2276 }
2277
2278 private void startTopAnimation() {
2279 int previousEndValue = mEndAnimationRect.top;
2280 int newEndValue = mBackgroundBounds.top;
2281 ObjectAnimator previousAnimator = mTopAnimator;
2282 if (previousAnimator != null && previousEndValue == newEndValue) {
2283 return;
2284 }
2285 if (!mAnimateNextBackgroundTop) {
2286 // just a local update was performed
2287 if (previousAnimator != null) {
2288 // we need to increase all animation keyframes of the previous animator by the
2289 // relative change to the end value
2290 int previousStartValue = mStartAnimationRect.top;
2291 PropertyValuesHolder[] values = previousAnimator.getValues();
2292 values[0].setIntValues(previousStartValue, newEndValue);
2293 mStartAnimationRect.top = previousStartValue;
2294 mEndAnimationRect.top = newEndValue;
2295 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2296 return;
2297 } else {
2298 // no new animation needed, let's just apply the value
2299 setBackgroundTop(newEndValue);
2300 return;
2301 }
2302 }
2303 if (previousAnimator != null) {
2304 previousAnimator.cancel();
2305 }
2306 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2307 mCurrentBounds.top, newEndValue);
2308 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2309 animator.setInterpolator(interpolator);
2310 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2311 // remove the tag when the animation is finished
2312 animator.addListener(new AnimatorListenerAdapter() {
2313 @Override
2314 public void onAnimationEnd(Animator animation) {
2315 mStartAnimationRect.top = -1;
2316 mEndAnimationRect.top = -1;
2317 mTopAnimator = null;
2318 }
2319 });
2320 animator.start();
2321 mStartAnimationRect.top = mCurrentBounds.top;
2322 mEndAnimationRect.top = newEndValue;
2323 mTopAnimator = animator;
2324 }
2325
2326 private void startBottomAnimation() {
2327 int previousStartValue = mStartAnimationRect.bottom;
2328 int previousEndValue = mEndAnimationRect.bottom;
2329 int newEndValue = mBackgroundBounds.bottom;
2330 ObjectAnimator previousAnimator = mBottomAnimator;
2331 if (previousAnimator != null && previousEndValue == newEndValue) {
2332 return;
2333 }
2334 if (!mAnimateNextBackgroundBottom) {
2335 // just a local update was performed
2336 if (previousAnimator != null) {
2337 // we need to increase all animation keyframes of the previous animator by the
2338 // relative change to the end value
2339 PropertyValuesHolder[] values = previousAnimator.getValues();
2340 values[0].setIntValues(previousStartValue, newEndValue);
2341 mStartAnimationRect.bottom = previousStartValue;
2342 mEndAnimationRect.bottom = newEndValue;
2343 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2344 return;
2345 } else {
2346 // no new animation needed, let's just apply the value
2347 setBackgroundBottom(newEndValue);
2348 return;
2349 }
2350 }
2351 if (previousAnimator != null) {
2352 previousAnimator.cancel();
2353 }
2354 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2355 mCurrentBounds.bottom, newEndValue);
2356 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2357 animator.setInterpolator(interpolator);
2358 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2359 // remove the tag when the animation is finished
2360 animator.addListener(new AnimatorListenerAdapter() {
2361 @Override
2362 public void onAnimationEnd(Animator animation) {
2363 mStartAnimationRect.bottom = -1;
2364 mEndAnimationRect.bottom = -1;
2365 mBottomAnimator = null;
2366 }
2367 });
2368 animator.start();
2369 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2370 mEndAnimationRect.bottom = newEndValue;
2371 mBottomAnimator = animator;
2372 }
2373
2374 private void setBackgroundTop(int top) {
2375 mCurrentBounds.top = top;
2376 applyCurrentBackgroundBounds();
2377 }
2378
2379 public void setBackgroundBottom(int bottom) {
2380 mCurrentBounds.bottom = bottom;
2381 applyCurrentBackgroundBounds();
2382 }
2383
2384 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002385 // If the background of the notification is not being drawn, then there is no need to
2386 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2387 if (!mShouldDrawNotificationBackground) {
2388 return;
2389 }
2390
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002391 final boolean awake = mDarkAmount != 0 || mAmbientState.isDark();
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002392 mScrimController.setExcludedBackgroundArea(
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002393 mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002394 : mCurrentBounds);
Selim Cinek614576e2016-01-20 10:54:09 -08002395 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002396 }
2397
2398 /**
2399 * Update the background bounds to the new desired bounds
2400 */
2401 private void updateBackgroundBounds() {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01002402 if (mAmbientState.isPanelFullWidth()) {
2403 mBackgroundBounds.left = 0;
2404 mBackgroundBounds.right = getWidth();
2405 } else {
2406 getLocationInWindow(mTempInt2);
2407 mBackgroundBounds.left = mTempInt2[0];
2408 mBackgroundBounds.right = mTempInt2[0] + getWidth();
2409 }
Selim Cinekb95fd182017-12-21 13:03:32 -08002410 mBackgroundBounds.left += mSidePaddings;
2411 mBackgroundBounds.right -= mSidePaddings;
Selim Cinek614576e2016-01-20 10:54:09 -08002412 if (!mIsExpanded) {
2413 mBackgroundBounds.top = 0;
2414 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002415 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002416 }
2417 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002418 int top = 0;
2419 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002420 // Round Y up to avoid seeing the background during animation
2421 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002422 if (mAnimateNextBackgroundTop
2423 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2424 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002425 // we're ending up at the same location as we are now, lets just skip the animation
2426 top = finalTranslationY;
2427 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002428 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002429 }
2430 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002431 ActivatableNotificationView lastView =
2432 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2433 ? mShelf
2434 : mLastVisibleBackgroundChild;
2435 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002436 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002437 int finalTranslationY;
2438 if (lastView == mShelf) {
2439 finalTranslationY = (int) mShelf.getTranslationY();
2440 } else {
2441 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2442 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002443 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002444 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek6811d722016-01-19 17:53:12 -08002445 finalBottom = Math.min(finalBottom, getHeight());
Selim Cinek614576e2016-01-20 10:54:09 -08002446 if (mAnimateNextBackgroundBottom
2447 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2448 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002449 // we're ending up at the same location as we are now, lets just skip the animation
2450 bottom = finalBottom;
2451 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002452 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2453 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002454 bottom = Math.min(bottom, getHeight());
2455 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002456 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002457 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002458 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002459 }
Selim Cinek355652a2016-12-07 13:32:12 -08002460 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002461 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002462 } else {
2463 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002464 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002465 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002466 mBackgroundBounds.top = top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002467 mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
Selim Cinek6811d722016-01-19 17:53:12 -08002468 }
2469
Selim Cinek614576e2016-01-20 10:54:09 -08002470 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2471 int childCount = getChildCount();
2472 for (int i = 0; i < childCount; i++) {
2473 View child = getChildAt(i);
2474 if (child.getVisibility() != View.GONE
2475 && child instanceof ExpandableNotificationRow) {
2476 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2477 if (row.isPinned()) {
2478 return row;
2479 }
2480 }
2481 }
2482 return null;
2483 }
2484
2485 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002486 int childCount = getChildCount();
2487 for (int i = childCount - 1; i >= 0; i--) {
2488 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002489 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2490 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002491 return (ActivatableNotificationView) child;
2492 }
2493 }
2494 return null;
2495 }
2496
Selim Cinek614576e2016-01-20 10:54:09 -08002497 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002498 int childCount = getChildCount();
2499 for (int i = 0; i < childCount; i++) {
2500 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002501 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2502 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002503 return (ActivatableNotificationView) child;
2504 }
2505 }
2506 return null;
2507 }
2508
Selim Cinek67b22602014-03-10 15:40:16 +01002509 /**
2510 * Fling the scroll view
2511 *
2512 * @param velocityY The initial velocity in the Y direction. Positive
2513 * numbers mean that the finger/cursor is moving down the screen,
2514 * which means we want to scroll towards the top.
2515 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002516 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002517 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002518 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002519
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002520 float topAmount = getCurrentOverScrollAmount(true);
2521 float bottomAmount = getCurrentOverScrollAmount(false);
2522 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002523 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002524 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002525 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002526 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002527 * mOverflingDistance + topAmount;
2528 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002529 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002530 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002531 mMaxOverScroll = Math.abs(velocityY) / 1000f
2532 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2533 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002534 } else {
2535 // it will be set once we reach the boundary
2536 mMaxOverScroll = 0.0f;
2537 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002538 int minScrollY = Math.max(0, scrollRange);
2539 if (mExpandedInThisMotion) {
2540 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2541 }
Selim Cinek9212de82017-02-06 16:04:28 -08002542 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2543 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002544
Selim Cinek9212de82017-02-06 16:04:28 -08002545 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002546 }
2547 }
2548
Selim Cinek1408eb52014-06-02 14:45:38 +02002549 /**
2550 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2551 * overScroll view (i.e QS).
2552 */
2553 private boolean shouldOverScrollFling(int initialVelocity) {
2554 float topOverScroll = getCurrentOverScrollAmount(true);
2555 return mScrolledToTopOnFirstDown
2556 && !mExpandedInThisMotion
2557 && topOverScroll > mMinTopOverScrollToEscape
2558 && initialVelocity > 0;
2559 }
2560
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002561 /**
2562 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2563 * account.
2564 *
2565 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002566 * @param animate whether to animate the change
2567 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2568 * {@code qsHeight} is the final top padding
2569 */
Jason Monk16ac3772016-02-10 15:39:21 -05002570 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002571 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002572 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002573 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002574 if (topPadding + minStackHeight > getHeight()) {
2575 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002576 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002577 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002578 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002579 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002580 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002581 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002582 }
2583
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002584 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002585 if (isHeadsUpTransition()) {
2586 return getTopHeadsUpPinnedHeight();
2587 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002588 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002589 }
2590
Selim Cineka981d082016-08-03 14:45:31 -07002591 public int getFirstChildIntrinsicHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002592 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002593 int firstChildMinHeight = firstChild != null
2594 ? firstChild.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002595 : mEmptyShadeView != null
Selim Cineka981d082016-08-03 14:45:31 -07002596 ? mEmptyShadeView.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002597 : mCollapsedSize;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002598 if (mOwnScrollY > 0) {
2599 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2600 }
Selim Cinek94c2d822016-07-13 18:50:04 -07002601 return firstChildMinHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002602 }
2603
2604 public float getTopPaddingOverflow() {
2605 return mTopPaddingOverflow;
2606 }
2607
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002608 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002609 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002610 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002611 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002612 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002613 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002614 shelfHeight = mShelf.getIntrinsicHeight();
2615 }
2616 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002617 }
2618
Selim Cinek1408eb52014-06-02 14:45:38 +02002619 private int clampPadding(int desiredPadding) {
2620 return Math.max(desiredPadding, mIntrinsicPadding);
2621 }
2622
Selim Cinekfed1ab62014-06-17 14:10:33 -07002623 private float getRubberBandFactor(boolean onTop) {
2624 if (!onTop) {
2625 return RUBBER_BAND_FACTOR_NORMAL;
2626 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002627 if (mExpandedInThisMotion) {
2628 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002629 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002630 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2631 } else if (mScrolledToTopOnFirstDown) {
2632 return 1.0f;
2633 }
2634 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002635 }
2636
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002637 /**
2638 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2639 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2640 * overscroll view (e.g. expand QS).
2641 */
2642 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002643 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002644 || !mScrolledToTopOnFirstDown;
2645 }
2646
Selim Cinek67b22602014-03-10 15:40:16 +01002647 private void endDrag() {
2648 setIsBeingDragged(false);
2649
2650 recycleVelocityTracker();
2651
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002652 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2653 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2654 }
2655 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2656 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2657 }
Selim Cinek67b22602014-03-10 15:40:16 +01002658 }
2659
Jorim Jaggi56306252014-07-03 00:40:09 +02002660 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2661 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2662 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2663 }
2664
Selim Cinek67b22602014-03-10 15:40:16 +01002665 @Override
2666 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002667 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002668 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002669 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002670 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002671 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2672 }
Selim Cinek67b22602014-03-10 15:40:16 +01002673 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002674 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002675 scrollWantsIt = onInterceptTouchEventScroll(ev);
2676 }
2677 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002678 if (!mIsBeingDragged
2679 && !mExpandingNotification
2680 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002681 && !mOnlyScrollingInThisMotion
2682 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002683 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2684 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002685 // Check if we need to clear any snooze leavebehinds
2686 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002687 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07002688 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2689 && !scrollWantsIt) {
2690 mCheckForLeavebehind = false;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002691 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
2692 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2693 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07002694 }
2695 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2696 mCheckForLeavebehind = true;
2697 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002698 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2699 }
2700
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002701 private void handleEmptySpaceClick(MotionEvent ev) {
2702 switch (ev.getActionMasked()) {
2703 case MotionEvent.ACTION_MOVE:
2704 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2705 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2706 mTouchIsClick = false;
2707 }
2708 break;
2709 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002710 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002711 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2712 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2713 }
2714 break;
2715 }
2716 }
2717
Selim Cinek1408eb52014-06-02 14:45:38 +02002718 private void initDownStates(MotionEvent ev) {
2719 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2720 mExpandedInThisMotion = false;
2721 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002722 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002723 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002724 mTouchIsClick = true;
2725 mInitialTouchX = ev.getX();
2726 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002727 }
Selim Cinek67b22602014-03-10 15:40:16 +01002728 }
2729
Selim Cinekef5127e2015-12-21 16:55:58 -08002730 public void setChildTransferInProgress(boolean childTransferInProgress) {
2731 mChildTransferInProgress = childTransferInProgress;
2732 }
2733
Christoph Studer068f5922014-04-08 17:43:07 -04002734 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002735 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002736 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002737 // we only call our internal methods if this is actually a removal and not just a
2738 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002739 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002740 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002741 }
2742 }
2743
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002744 @Override
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002745 public void cleanUpViewState(View child) {
2746 if (child == mTranslatingParentView) {
2747 mTranslatingParentView = null;
2748 }
2749 mCurrentStackScrollState.removeViewStateForView(child);
2750 }
2751
Selim Cinek197823d2016-03-24 13:06:00 -07002752 @Override
2753 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2754 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2755 if (disallowIntercept) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002756 cancelLongPress();
Selim Cinek197823d2016-03-24 13:06:00 -07002757 }
2758 }
2759
Selim Cineka5703182016-05-11 21:23:16 -04002760 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002761 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002762 // This is only a position change, don't do anything special
2763 return;
2764 }
Selim Cinekd1395642016-04-28 12:22:42 -07002765 ExpandableView expandableView = (ExpandableView) child;
2766 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002767 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002768 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002769 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002770 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002771 if (!mSwipedOutViews.contains(child)
2772 || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002773 container.addTransientView(child, 0);
2774 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002775 }
2776 } else {
2777 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002778 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002779 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002780
Selim Cineke9bad242016-06-15 11:46:37 -07002781 focusNextViewIfFocused(child);
2782 }
2783
2784 private void focusNextViewIfFocused(View view) {
2785 if (view instanceof ExpandableNotificationRow) {
2786 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2787 if (row.shouldRefocusOnDismiss()) {
2788 View nextView = row.getChildAfterViewWhenDismissed();
2789 if (nextView == null) {
2790 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2791 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2792 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002793 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002794 }
2795 if (nextView != null) {
2796 nextView.requestAccessibilityFocus();
2797 }
2798 }
2799 }
2800
Selim Cinekc27437b2014-05-14 10:23:33 +02002801 }
2802
Selim Cinekb5605e52015-02-20 18:21:41 +01002803 private boolean isChildInGroup(View child) {
2804 return child instanceof ExpandableNotificationRow
2805 && mGroupManager.isChildInGroupWithSummary(
2806 ((ExpandableNotificationRow) child).getStatusBarNotification());
2807 }
2808
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002809 /**
2810 * Generate a remove animation for a child view.
2811 *
2812 * @param child The view to generate the remove animation for.
2813 * @return Whether an animation was generated.
2814 */
2815 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002816 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002817 mAddedHeadsUpChildren.remove(child);
2818 return false;
2819 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002820 if (isClickedHeadsUp(child)) {
2821 // An animation is already running, add it to the Overlay
2822 mClearOverlayViewsWhenFinished.add(child);
2823 return true;
2824 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002825 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002826 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002827 // Generate Animations
2828 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002829 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002830 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002831 } else {
2832 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002833 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002834 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002835 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002836 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002837 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002838 }
2839
Selim Cinek0fccc722015-07-29 17:04:36 -07002840 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002841 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002842 }
2843
Selim Cineke0890e52015-06-17 11:17:08 -07002844 /**
2845 * Remove a removed child view from the heads up animations if it was just added there
2846 *
2847 * @return whether any child was removed from the list to animate
2848 */
2849 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2850 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002851 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2852 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002853 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002854 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002855 mTmpList.add(eventPair);
2856 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002857 }
2858 }
Selim Cineke0890e52015-06-17 11:17:08 -07002859 if (hasAddEvent) {
2860 // This child was just added lets remove all events.
2861 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002862 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002863 }
2864 mTmpList.clear();
2865 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002866 }
2867
Selim Cinek572bbd42014-04-25 16:43:27 +02002868 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002869 * @param child the child to query
2870 * @return whether a view is not a top level child but a child notification and that group is
2871 * not expanded
2872 */
2873 private boolean isChildInInvisibleGroup(View child) {
2874 if (child instanceof ExpandableNotificationRow) {
2875 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2876 ExpandableNotificationRow groupSummary =
2877 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2878 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002879 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002880 }
2881 }
2882 return false;
2883 }
2884
2885 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002886 * Updates the scroll position when a child was removed
2887 *
2888 * @param removedChild the removed child
2889 */
Selim Cinek61633a82016-01-25 15:54:10 -08002890 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002891 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002892 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2893 int padding;
2894 if (increasedPaddingAmount >= 0) {
2895 padding = (int) NotificationUtils.interpolate(
2896 mPaddingBetweenElements,
2897 mIncreasedPaddingBetweenElements,
2898 increasedPaddingAmount);
2899 } else {
2900 padding = (int) NotificationUtils.interpolate(
2901 0,
2902 mPaddingBetweenElements,
2903 1.0f + increasedPaddingAmount);
2904 }
Selim Cinek61633a82016-01-25 15:54:10 -08002905 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002906 int endPosition = startingPosition + childHeight;
2907 if (endPosition <= mOwnScrollY) {
2908 // This child is fully scrolled of the top, so we have to deduct its height from the
2909 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002910 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002911 } else if (startingPosition < mOwnScrollY) {
2912 // This child is currently being scrolled into, set the scroll position to the start of
2913 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002914 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002915 }
2916 }
2917
Selim Cinekd7c4e002014-07-04 18:36:42 +02002918 private int getIntrinsicHeight(View view) {
2919 if (view instanceof ExpandableView) {
2920 ExpandableView expandableView = (ExpandableView) view;
2921 return expandableView.getIntrinsicHeight();
2922 }
2923 return view.getHeight();
2924 }
2925
Selim Cinek1f624952017-06-08 19:11:50 -07002926 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002927 ExpandableNotificationRow childInGroup = null;
2928 ExpandableNotificationRow requestedRow = null;
2929 if (isChildInGroup(requestedView)) {
2930 // We're asking for a child in a group. Calculate the position of the parent first,
2931 // then within the parent.
2932 childInGroup = (ExpandableNotificationRow) requestedView;
2933 requestedView = requestedRow = childInGroup.getNotificationParent();
2934 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002935 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002936 float previousPaddingRequest = mPaddingBetweenElements;
2937 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002938 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002939 ExpandableView child = (ExpandableView) getChildAt(i);
2940 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002941 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002942 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002943 float padding;
2944 if (increasedPaddingAmount >= 0.0f) {
2945 padding = (int) NotificationUtils.interpolate(
2946 previousPaddingRequest,
2947 mIncreasedPaddingBetweenElements,
2948 increasedPaddingAmount);
2949 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002950 mPaddingBetweenElements,
2951 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002952 increasedPaddingAmount);
2953 } else {
2954 int ownPadding = (int) NotificationUtils.interpolate(
2955 0,
2956 mPaddingBetweenElements,
2957 1.0f + increasedPaddingAmount);
2958 if (previousPaddingAmount > 0.0f) {
2959 padding = (int) NotificationUtils.interpolate(
2960 ownPadding,
2961 mIncreasedPaddingBetweenElements,
2962 previousPaddingAmount);
2963 } else {
2964 padding = ownPadding;
2965 }
2966 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08002967 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002968 if (position != 0) {
2969 position += padding;
2970 }
2971 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002972 }
Adrian Roos4a579672016-05-24 16:54:37 -07002973 if (child == requestedView) {
2974 if (requestedRow != null) {
2975 position += requestedRow.getPositionOfChild(childInGroup);
2976 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002977 return position;
2978 }
Selim Cinek61633a82016-01-25 15:54:10 -08002979 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02002980 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002981 }
2982 }
2983 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02002984 }
2985
2986 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002987 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02002988 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002989 onViewAddedInternal(child);
2990 }
2991
Selim Cinek614576e2016-01-20 10:54:09 -08002992 private void updateFirstAndLastBackgroundViews() {
2993 ActivatableNotificationView firstChild = getFirstChildWithBackground();
2994 ActivatableNotificationView lastChild = getLastChildWithBackground();
2995 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek29aab962018-02-27 17:05:45 -08002996 mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
2997 mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
Selim Cinek614576e2016-01-20 10:54:09 -08002998 } else {
2999 mAnimateNextBackgroundTop = false;
3000 mAnimateNextBackgroundBottom = false;
3001 }
3002 mFirstVisibleBackgroundChild = firstChild;
3003 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08003004 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek29aab962018-02-27 17:05:45 -08003005 mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
3006 mLastVisibleBackgroundChild);
Selim Cinek515b2032017-11-15 10:20:19 -08003007 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003008 }
3009
Selim Cinekb5605e52015-02-20 18:21:41 +01003010 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003011 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02003012 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003013 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003014 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003015 updateChronometerForChild(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003016 }
3017
Selim Cinekd06c41c2015-07-06 14:51:36 -07003018 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04003019 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003020 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04003021 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003022 }
3023 }
3024
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003025 @Override
Selim Cinekd1395642016-04-28 12:22:42 -07003026 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
3027 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003028 }
3029
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003030 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003031 public void notifyGroupChildAdded(View row) {
3032 onViewAddedInternal(row);
3033 }
3034
Jorim Jaggi75c95042014-05-16 19:09:59 +02003035 public void setAnimationsEnabled(boolean animationsEnabled) {
3036 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003037 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003038 if (!animationsEnabled) {
3039 mSwipedOutViews.clear();
3040 mChildrenToRemoveAnimated.clear();
3041 clearTemporaryViewsInGroup(this);
3042 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003043 }
3044
3045 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003046 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003047 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003048 int childCount = getChildCount();
3049 for (int i = 0; i < childCount; i++) {
3050 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003051 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003052 updateAnimationState(running, child);
3053 }
3054 }
3055
Selim Cinek51ae05d2014-09-09 15:51:38 +02003056 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003057 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003058 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003059 }
3060
Selim Cinek2627d722018-01-19 12:16:49 -08003061 @Override
3062 public void setExpandingNotification(ExpandableNotificationRow row) {
3063 mAmbientState.setExpandingNotification(row);
3064 requestChildrenUpdate();
3065 }
3066
3067 @Override
Selim Cinek8875de12018-03-22 10:14:32 -07003068 public void bindRow(ExpandableNotificationRow row) {
3069 row.setHeadsUpAnimatingAwayListener(animatingAway
3070 -> mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway));
3071 }
3072
3073 @Override
Selim Cinek2627d722018-01-19 12:16:49 -08003074 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3075 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3076 requestChildrenUpdate();
3077 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003078
Selim Cinekcab4a602014-09-03 14:47:57 +02003079 private void updateAnimationState(boolean running, View child) {
3080 if (child instanceof ExpandableNotificationRow) {
3081 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3082 row.setIconAnimationRunning(running);
3083 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003084 }
3085
3086 public boolean isAddOrRemoveAnimationPending() {
3087 return mNeedsAnimation
3088 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3089 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003090
3091 @Override
Jorim Jaggif6411742014-08-05 17:10:43 +00003092 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003093 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003094 // Generate Animations
3095 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003096 if (fromMoreCard) {
3097 mFromMoreCardAdditions.add(child);
3098 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003099 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003100 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003101 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003102 mAddedHeadsUpChildren.add(child);
3103 mChildrenToAddAnimated.remove(child);
3104 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003105 }
3106
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003107 @Override
Selim Cinek572bbd42014-04-25 16:43:27 +02003108 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003109 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003110
3111 if (currentIndex == -1) {
3112 boolean isTransient = false;
3113 if (child instanceof ExpandableNotificationRow
3114 && ((ExpandableNotificationRow)child).getTransientContainer() != null) {
3115 isTransient = true;
3116 }
3117 Log.e(TAG, "Attempting to re-position "
3118 + (isTransient ? "transient" : "")
3119 + " view {"
3120 + child
3121 + "}");
3122 return;
3123 }
3124
Dan Sandlereceda3d2014-07-21 15:35:01 -04003125 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003126 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08003127 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003128 removeView(child);
3129 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08003130 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003131 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003132 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003133 mChildrenChangingPositions.add(child);
3134 mNeedsAnimation = true;
3135 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003136 }
3137 }
3138
Selim Cinekf4c19962014-05-01 21:55:31 +02003139 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003140 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003141 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003142 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003143 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003144 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003145 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003146 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
3147 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003148 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003149 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003150 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003151 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003152 } else {
3153 applyCurrentState();
3154 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003155 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003156 }
3157
3158 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003159 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003160 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003161 generateChildAdditionEvents();
3162 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02003163 generateSnapBackEvents();
3164 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003165 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003166 generateActivateEvent();
3167 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003168 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003169 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003170 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003171 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003172 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003173 generateAnimateEverythingEvent();
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07003174 generatePulsingAnimationEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003175 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003176 }
3177
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003178 private void generateHeadsUpAnimationEvents() {
3179 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003180 ExpandableNotificationRow row = eventPair.first;
3181 boolean isHeadsUp = eventPair.second;
3182 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3183 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003184 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003185 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003186 type = row.wasJustClicked()
3187 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3188 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003189 if (row.isChildInGroup()) {
3190 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003191 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003192 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003193 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08003194 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04003195 if (viewState == null) {
3196 // A view state was never generated for this view, so we don't need to animate
3197 // this. This may happen with notification children.
3198 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003199 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003200 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3201 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3202 // Our custom add animation
3203 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3204 } else {
3205 // Normal add animation
3206 type = AnimationEvent.ANIMATION_TYPE_ADD;
3207 }
3208 onBottom = !pinnedAndClosed;
3209 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003210 }
3211 AnimationEvent event = new AnimationEvent(row, type);
3212 event.headsUpFromBottom = onBottom;
3213 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003214 }
3215 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003216 mAddedHeadsUpChildren.clear();
3217 }
3218
Selim Cinekbbcebde2016-11-09 18:28:20 -08003219 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003220 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3221 return false;
3222 }
3223 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003224 }
3225
Selim Cinekb5605e52015-02-20 18:21:41 +01003226 private void generateGroupExpansionEvent() {
3227 // Generate a group expansion/collapsing event if there is such a group at all
3228 if (mExpandedGroupView != null) {
3229 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3230 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3231 mExpandedGroupView = null;
3232 }
3233 }
3234
Selim Cineka5e211b2014-08-11 17:35:48 +02003235 private void generateViewResizeEvent() {
3236 if (mNeedViewResizeAnimation) {
3237 mAnimationEvents.add(
3238 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3239 }
3240 mNeedViewResizeAnimation = false;
3241 }
3242
Selim Cinekeb973562014-05-02 17:07:49 +02003243 private void generateSnapBackEvents() {
3244 for (View child : mSnappedBackChildren) {
3245 mAnimationEvents.add(new AnimationEvent(child,
3246 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3247 }
3248 mSnappedBackChildren.clear();
3249 }
3250
3251 private void generateDragEvents() {
3252 for (View child : mDragAnimPendingChildren) {
3253 mAnimationEvents.add(new AnimationEvent(child,
3254 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3255 }
3256 mDragAnimPendingChildren.clear();
3257 }
3258
Selim Cinek572bbd42014-04-25 16:43:27 +02003259 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003260 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003261 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003262
3263 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003264 float removedTranslation = child.getTranslationY();
3265 boolean ignoreChildren = true;
3266 if (child instanceof ExpandableNotificationRow) {
3267 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3268 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3269 removedTranslation = row.getTranslationWhenRemoved();
3270 ignoreChildren = false;
3271 }
Selim Cinek51052042017-07-04 12:07:55 +02003272 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003273 }
Selim Cinek51052042017-07-04 12:07:55 +02003274 if (!childWasSwipedOut) {
3275 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003276 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Selim Cinek51052042017-07-04 12:07:55 +02003277 }
3278 int animationType = childWasSwipedOut
3279 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3280 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3281 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003282 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3283 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003284 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003285 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003286 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003287 mChildrenToRemoveAnimated.clear();
3288 }
3289
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003290 private void generatePositionChangeEvents() {
3291 for (View child : mChildrenChangingPositions) {
3292 mAnimationEvents.add(new AnimationEvent(child,
3293 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3294 }
3295 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003296 if (mGenerateChildOrderChangedEvent) {
3297 mAnimationEvents.add(new AnimationEvent(null,
3298 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3299 mGenerateChildOrderChangedEvent = false;
3300 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003301 }
3302
Selim Cinek572bbd42014-04-25 16:43:27 +02003303 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003304 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003305 if (mFromMoreCardAdditions.contains(child)) {
3306 mAnimationEvents.add(new AnimationEvent(child,
3307 AnimationEvent.ANIMATION_TYPE_ADD,
3308 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3309 } else {
3310 mAnimationEvents.add(new AnimationEvent(child,
3311 AnimationEvent.ANIMATION_TYPE_ADD));
3312 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003313 }
3314 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003315 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003316 }
3317
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003318 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003319 if (mTopPaddingNeedsAnimation) {
3320 mAnimationEvents.add(
3321 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
3322 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003323 mTopPaddingNeedsAnimation = false;
3324 }
3325
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003326 private void generateActivateEvent() {
3327 if (mActivateNeedsAnimation) {
3328 mAnimationEvents.add(
3329 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3330 }
3331 mActivateNeedsAnimation = false;
3332 }
3333
Selim Cinekd9acca52014-09-01 22:33:25 +02003334 private void generateAnimateEverythingEvent() {
3335 if (mEverythingNeedsAnimation) {
3336 mAnimationEvents.add(
3337 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3338 }
3339 mEverythingNeedsAnimation = false;
3340 }
3341
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003342 private void generateDimmedEvent() {
3343 if (mDimmedNeedsAnimation) {
3344 mAnimationEvents.add(
3345 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3346 }
3347 mDimmedNeedsAnimation = false;
3348 }
3349
Jorim Jaggiae441282014-08-01 02:45:18 +02003350 private void generateHideSensitiveEvent() {
3351 if (mHideSensitiveNeedsAnimation) {
3352 mAnimationEvents.add(
3353 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3354 }
3355 mHideSensitiveNeedsAnimation = false;
3356 }
3357
John Spurlockbf370992014-06-17 13:58:31 -04003358 private void generateDarkEvent() {
3359 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003360 AnimationEvent ev = new AnimationEvent(null,
3361 AnimationEvent.ANIMATION_TYPE_DARK,
3362 new AnimationFilter()
3363 .animateDark()
3364 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003365 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3366 mAnimationEvents.add(ev);
Lucas Dupin16cfe452018-02-08 13:14:50 -08003367 startBackgroundFade();
John Spurlockbf370992014-06-17 13:58:31 -04003368 }
3369 mDarkNeedsAnimation = false;
3370 }
3371
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003372 private void generateGoToFullShadeEvent() {
3373 if (mGoToFullShadeNeedsAnimation) {
3374 mAnimationEvents.add(
3375 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3376 }
3377 mGoToFullShadeNeedsAnimation = false;
3378 }
3379
Selim Cinek67b22602014-03-10 15:40:16 +01003380 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003381 if (!isScrollingEnabled()) {
3382 return false;
3383 }
Selim Cinek67b22602014-03-10 15:40:16 +01003384 /*
3385 * This method JUST determines whether we want to intercept the motion.
3386 * If we return true, onMotionEvent will be called and we do the actual
3387 * scrolling there.
3388 */
3389
3390 /*
3391 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003392 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003393 * motion.
3394 */
3395 final int action = ev.getAction();
3396 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3397 return true;
3398 }
3399
Selim Cinek67b22602014-03-10 15:40:16 +01003400 switch (action & MotionEvent.ACTION_MASK) {
3401 case MotionEvent.ACTION_MOVE: {
3402 /*
3403 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003404 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003405 */
3406
3407 /*
3408 * Locally do absolute value. mLastMotionY is set to the y value
3409 * of the down event.
3410 */
3411 final int activePointerId = mActivePointerId;
3412 if (activePointerId == INVALID_POINTER) {
3413 // If we don't have a valid id, the touch down wasn't on content.
3414 break;
3415 }
3416
3417 final int pointerIndex = ev.findPointerIndex(activePointerId);
3418 if (pointerIndex == -1) {
3419 Log.e(TAG, "Invalid pointerId=" + activePointerId
3420 + " in onInterceptTouchEvent");
3421 break;
3422 }
3423
3424 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003425 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003426 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003427 final int xDiff = Math.abs(x - mDownX);
3428 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003429 setIsBeingDragged(true);
3430 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003431 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003432 initVelocityTrackerIfNotExists();
3433 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003434 }
3435 break;
3436 }
3437
3438 case MotionEvent.ACTION_DOWN: {
3439 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303440 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07003441 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01003442 setIsBeingDragged(false);
3443 recycleVelocityTracker();
3444 break;
3445 }
3446
3447 /*
3448 * Remember location of down touch.
3449 * ACTION_DOWN always refers to pointer index 0.
3450 */
3451 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003452 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003453 mActivePointerId = ev.getPointerId(0);
3454
3455 initOrResetVelocityTracker();
3456 mVelocityTracker.addMovement(ev);
3457 /*
3458 * If being flinged and user touches the screen, initiate drag;
3459 * otherwise don't. mScroller.isFinished should be false when
3460 * being flinged.
3461 */
3462 boolean isBeingDragged = !mScroller.isFinished();
3463 setIsBeingDragged(isBeingDragged);
3464 break;
3465 }
3466
3467 case MotionEvent.ACTION_CANCEL:
3468 case MotionEvent.ACTION_UP:
3469 /* Release the drag */
3470 setIsBeingDragged(false);
3471 mActivePointerId = INVALID_POINTER;
3472 recycleVelocityTracker();
3473 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08003474 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01003475 }
3476 break;
3477 case MotionEvent.ACTION_POINTER_UP:
3478 onSecondaryPointerUp(ev);
3479 break;
3480 }
3481
3482 /*
3483 * The only time we want to intercept motion events is if we are in the
3484 * drag mode.
3485 */
3486 return mIsBeingDragged;
3487 }
3488
Muyuan Li333a4fc2016-04-16 17:13:46 -07003489 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3490 return new StackScrollAlgorithm(context);
3491 }
3492
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003493 /**
3494 * @return Whether the specified motion event is actually happening over the content.
3495 */
3496 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003497 return isInContentBounds(event.getY());
3498 }
3499
3500 /**
3501 * @return Whether a y coordinate is inside the content.
3502 */
3503 public boolean isInContentBounds(float y) {
3504 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003505 }
3506
Selim Cinek67b22602014-03-10 15:40:16 +01003507 private void setIsBeingDragged(boolean isDragged) {
3508 mIsBeingDragged = isDragged;
3509 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003510 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003511 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003512 }
3513 }
3514
3515 @Override
3516 public void onWindowFocusChanged(boolean hasWindowFocus) {
3517 super.onWindowFocusChanged(hasWindowFocus);
3518 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003519 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003520 }
3521 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003522
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003523 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003524 public void clearChildFocus(View child) {
3525 super.clearChildFocus(child);
3526 if (mForcedScroll == child) {
3527 mForcedScroll = null;
3528 }
3529 }
3530
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003531 public void requestDisallowLongPress() {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003532 cancelLongPress();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003533 }
3534
Adrian Roosfa139752016-04-27 09:59:08 -07003535 public void requestDisallowDismiss() {
3536 mDisallowDismissInThisMotion = true;
3537 }
3538
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003539 public void cancelLongPress() {
3540 mSwipeHelper.cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02003541 }
3542
Selim Cinekfab078b2014-03-27 22:45:58 +01003543 @Override
3544 public boolean isScrolledToTop() {
3545 return mOwnScrollY == 0;
3546 }
3547
3548 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003549 public boolean isScrolledToBottom() {
3550 return mOwnScrollY >= getScrollRange();
3551 }
3552
3553 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003554 public View getHostView() {
3555 return this;
3556 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003557
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003558 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003559 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003560 }
3561
Mady Mellorc2dbe492017-03-30 13:22:03 -07003562 public void checkSnoozeLeavebehind() {
3563 if (mCheckForLeavebehind) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09003564 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
3565 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3566 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003567 mCheckForLeavebehind = false;
3568 }
3569 }
3570
3571 public void resetCheckSnoozeLeavebehind() {
3572 mCheckForLeavebehind = true;
3573 }
3574
Selim Cinek1685e632014-04-08 02:27:49 +02003575 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003576 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003577 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003578 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02003579 }
3580
3581 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003582 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07003583 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08003584 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003585 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003586 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003587 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003588 clearTemporaryViews();
3589 clearUserLockedViews();
3590 }
3591 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01003592
Selim Cinek5b1591a2017-07-03 17:05:01 +02003593 private void clearUserLockedViews() {
3594 for (int i = 0; i < getChildCount(); i++) {
3595 ExpandableView child = (ExpandableView) getChildAt(i);
3596 if (child instanceof ExpandableNotificationRow) {
3597 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3598 row.setUserLocked(false);
3599 }
3600 }
3601 }
3602
3603 private void clearTemporaryViews() {
3604 // lets make sure nothing is in the overlay / transient anymore
Rohan Shah8ee53652018-04-05 11:13:50 -07003605 clearTemporaryViewsInGroup(this);
Selim Cinek5b1591a2017-07-03 17:05:01 +02003606 for (int i = 0; i < getChildCount(); i++) {
3607 ExpandableView child = (ExpandableView) getChildAt(i);
3608 if (child instanceof ExpandableNotificationRow) {
3609 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Rohan Shah8ee53652018-04-05 11:13:50 -07003610 clearTemporaryViewsInGroup(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003611 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003612 }
Selim Cinek1685e632014-04-08 02:27:49 +02003613 }
3614
Rohan Shah8ee53652018-04-05 11:13:50 -07003615 private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003616 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003617 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003618 }
Selim Cineka5703182016-05-11 21:23:16 -04003619 if (viewGroup != null) {
3620 viewGroup.getOverlay().clear();
3621 }
Selim Cinekd1395642016-04-28 12:22:42 -07003622 }
3623
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003624 public void onPanelTrackingStarted() {
3625 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003626 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003627 }
3628 public void onPanelTrackingStopped() {
3629 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003630 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003631 }
3632
Selim Cinekb24e0a92015-06-09 20:17:30 -07003633 public void resetScrollPosition() {
3634 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003635 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003636 }
3637
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003638 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003639 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003640 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003641 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003642 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003643 if (!mIsExpanded) {
3644 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003645 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08003646 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003647 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003648 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003649 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003650 }
3651 }
3652
3653 private void updateChronometers() {
3654 int childCount = getChildCount();
3655 for (int i = 0; i < childCount; i++) {
3656 updateChronometerForChild(getChildAt(i));
3657 }
3658 }
3659
3660 private void updateChronometerForChild(View child) {
3661 if (child instanceof ExpandableNotificationRow) {
3662 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3663 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003664 }
Selim Cinek1685e632014-04-08 02:27:49 +02003665 }
3666
Jorim Jaggibe565df2014-04-28 17:51:23 +02003667 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003668 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003669 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003670 updateScrollPositionOnExpandInBottom(view);
3671 clampScrollPosition();
Lucas Dupin60661a62018-04-12 10:50:13 -07003672 notifyHeightChangeListener(view, needsAnimation);
Selim Cinekbc243a92016-09-27 16:35:13 -07003673 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3674 ? (ExpandableNotificationRow) view
3675 : null;
3676 if (row != null && (row == mFirstVisibleBackgroundChild
3677 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3678 updateAlgorithmLayoutMinHeight();
3679 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003680 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003681 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003682 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003683 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003684 }
3685
Selim Cineka5e211b2014-08-11 17:35:48 +02003686 @Override
3687 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003688 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003689 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003690 }
3691
Selim Cinekf7a14c02014-07-07 14:01:46 +02003692 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07003693 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02003694 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003695 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003696 if (row.isSummaryWithChildren()) {
3697 return;
3698 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003699 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003700 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003701 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003702 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003703 }
Selim Cinekdb167372016-11-17 15:41:17 -08003704 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Anthony Chen9e05d462017-04-07 10:10:21 -07003705 if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08003706 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3707 }
3708 if (endPosition > layoutEnd) {
3709 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003710 mDisallowScrollingInThisMotion = true;
3711 }
3712 }
3713 }
3714 }
3715
Jorim Jaggibe565df2014-04-28 17:51:23 +02003716 public void setOnHeightChangedListener(
3717 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3718 this.mOnHeightChangedListener = mOnHeightChangedListener;
3719 }
3720
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003721 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3722 mOnEmptySpaceClickListener = listener;
3723 }
3724
Selim Cinek572bbd42014-04-25 16:43:27 +02003725 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003726 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003727 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003728 runAnimationFinishedRunnables();
Selim Cinek0fccc722015-07-29 17:04:36 -07003729 clearViewOverlays();
Selim Cinek8fc78752016-07-13 14:34:56 -07003730 clearHeadsUpDisappearRunning();
3731 }
3732
3733 private void clearHeadsUpDisappearRunning() {
3734 for (int i = 0; i < getChildCount(); i++) {
3735 View view = getChildAt(i);
3736 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003737 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003738 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003739 if (row.isSummaryWithChildren()) {
3740 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003741 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003742 }
3743 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003744 }
3745 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003746 }
3747
3748 private void clearViewOverlays() {
3749 for (View view : mClearOverlayViewsWhenFinished) {
Selim Cineka5703182016-05-11 21:23:16 -04003750 StackStateAnimator.removeFromOverlay(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003751 }
Selim Cinekc8df0642017-02-23 17:01:04 -08003752 mClearOverlayViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003753 }
3754
3755 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003756 for (Runnable runnable : mAnimationFinishedRunnables) {
3757 runnable.run();
3758 }
3759 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003760 }
3761
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003762 /**
3763 * See {@link AmbientState#setDimmed}.
3764 */
3765 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07003766 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003767 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003768 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003769 mDimmedNeedsAnimation = true;
3770 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003771 animateDimmed(dimmed);
3772 } else {
3773 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003774 }
3775 requestChildrenUpdate();
3776 }
3777
Selim Cinek8a9308d2017-08-24 09:31:08 -07003778 @VisibleForTesting
3779 boolean isDimmed() {
3780 return mAmbientState.isDimmed();
3781 }
3782
Selim Cinekd35c2792016-01-21 13:20:57 -08003783 private void setDimAmount(float dimAmount) {
3784 mDimAmount = dimAmount;
3785 updateBackgroundDimming();
3786 }
3787
3788 private void animateDimmed(boolean dimmed) {
3789 if (mDimAnimator != null) {
3790 mDimAnimator.cancel();
3791 }
3792 float target = dimmed ? 1.0f : 0.0f;
3793 if (target == mDimAmount) {
3794 return;
3795 }
3796 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3797 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3798 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3799 mDimAnimator.addListener(mDimEndListener);
3800 mDimAnimator.addUpdateListener(mDimUpdateListener);
3801 mDimAnimator.start();
3802 }
3803
Jorim Jaggiae441282014-08-01 02:45:18 +02003804 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3805 if (hideSensitive != mAmbientState.isHideSensitive()) {
3806 int childCount = getChildCount();
3807 for (int i = 0; i < childCount; i++) {
3808 ExpandableView v = (ExpandableView) getChildAt(i);
3809 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3810 }
3811 mAmbientState.setHideSensitive(hideSensitive);
3812 if (animate && mAnimationsEnabled) {
3813 mHideSensitiveNeedsAnimation = true;
3814 mNeedsAnimation = true;
3815 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08003816 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02003817 requestChildrenUpdate();
3818 }
3819 }
3820
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003821 /**
3822 * See {@link AmbientState#setActivatedChild}.
3823 */
Selim Cineka32ab602014-06-11 15:06:01 +02003824 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003825 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003826 if (mAnimationsEnabled) {
3827 mActivateNeedsAnimation = true;
3828 mNeedsAnimation = true;
3829 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003830 requestChildrenUpdate();
3831 }
3832
Selim Cineka32ab602014-06-11 15:06:01 +02003833 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003834 return mAmbientState.getActivatedChild();
3835 }
3836
Selim Cinek572bbd42014-04-25 16:43:27 +02003837 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003838 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003839 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003840 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02003841 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003842 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003843 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003844 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003845 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003846 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08003847 }
3848
3849 private void updateViewShadows() {
3850 // we need to work around an issue where the shadow would not cast between siblings when
3851 // their z difference is between 0 and 0.1
3852
3853 // Lefts first sort by Z difference
3854 for (int i = 0; i < getChildCount(); i++) {
3855 ExpandableView child = (ExpandableView) getChildAt(i);
3856 if (child.getVisibility() != GONE) {
3857 mTmpSortedChildren.add(child);
3858 }
3859 }
3860 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3861
3862 // Now lets update the shadow for the views
3863 ExpandableView previous = null;
3864 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3865 ExpandableView expandableView = mTmpSortedChildren.get(i);
3866 float translationZ = expandableView.getTranslationZ();
3867 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3868 float diff = otherZ - translationZ;
3869 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3870 // There is no fake shadow to be drawn
3871 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3872 } else {
3873 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003874 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3875 expandableView.setFakeShadowIntensity(
3876 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003877 previous.getOutlineAlpha(), (int) yLocation,
3878 previous.getOutlineTranslation());
3879 }
3880 previous = expandableView;
3881 }
3882
3883 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003884 }
3885
Lucas Dupine17ce522017-07-17 15:45:06 -07003886 /**
3887 * Update colors of "dismiss" and "empty shade" views.
3888 *
3889 * @param lightTheme True if light theme should be used.
3890 */
3891 public void updateDecorViews(boolean lightTheme) {
3892 if (lightTheme == mUsingLightTheme) {
3893 return;
3894 }
3895 mUsingLightTheme = lightTheme;
3896 Context context = new ContextThemeWrapper(mContext,
3897 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
3898 final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04003899 mFooterView.setTextColor(textColor);
Lucas Dupine17ce522017-07-17 15:45:06 -07003900 mEmptyShadeView.setTextColor(textColor);
3901 }
3902
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003903 public void goToFullShade(long delay) {
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04003904 if (mFooterView != null) {
3905 mFooterView.setInvisible();
Anthony Chen5e3742e2017-04-07 14:28:44 -07003906 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003907 mEmptyShadeView.setInvisible();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003908 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003909 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003910 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003911 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003912 }
3913
Selim Cinek1408eb52014-06-02 14:45:38 +02003914 public void cancelExpandHelper() {
3915 mExpandHelper.cancel();
3916 }
3917
3918 public void setIntrinsicPadding(int intrinsicPadding) {
3919 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07003920 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02003921 }
3922
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003923 public int getIntrinsicPadding() {
3924 return mIntrinsicPadding;
3925 }
3926
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003927 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003928 * @return the y position of the first notification
3929 */
3930 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003931 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003932 }
3933
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003934 @Override
3935 public boolean shouldDelayChildPressedState() {
3936 return true;
3937 }
3938
Jorim Jaggi457cc352014-06-02 22:47:42 +02003939 /**
John Spurlockbf370992014-06-17 13:58:31 -04003940 * See {@link AmbientState#setDark}.
3941 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003942 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02003943 if (mAmbientState.isDark() == dark) {
3944 return;
3945 }
John Spurlockbf370992014-06-17 13:58:31 -04003946 mAmbientState.setDark(dark);
3947 if (animate && mAnimationsEnabled) {
3948 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003949 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
John Spurlockbf370992014-06-17 13:58:31 -04003950 mNeedsAnimation = true;
Selim Cinek6811d722016-01-19 17:53:12 -08003951 } else {
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08003952 setDarkAmount(dark ? 1f : 0f);
Selim Cinek6811d722016-01-19 17:53:12 -08003953 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08003954 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08003955 requestChildrenUpdate();
3956 applyCurrentBackgroundBounds();
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003957 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003958 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04003959 }
3960
Lucas Dupin0cd882f2018-01-30 12:19:49 -08003961 private void updateAntiBurnInTranslation() {
Lucas Dupin15475702018-04-04 14:32:53 -07003962 setTranslationX(mAntiBurnInOffsetX * mDarkAmount);
Lucas Dupin0cd882f2018-01-30 12:19:49 -08003963 }
3964
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003965 /**
3966 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
3967 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
3968 * {@link #mAmbientState}'s dark mode is toggled.
3969 */
3970 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08003971 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07003972 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003973 }
3974
Lucas Dupind285cf02018-01-18 09:18:23 -08003975 private void setDarkAmount(float darkAmount) {
3976 mDarkAmount = darkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -07003977 boolean wasFullyDark = mAmbientState.isFullyDark();
3978 mAmbientState.setDarkAmount(darkAmount);
3979 if (mAmbientState.isFullyDark() != wasFullyDark) {
Lucas Dupin16cfe452018-02-08 13:14:50 -08003980 updateContentHeight();
Lucas Dupinb561eda2018-04-09 17:25:04 -07003981 DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
3982 if (mAmbientState.isFullyDark() && dozeParameters.shouldControlScreenOff()) {
3983 mShelf.fadeInTranslating();
3984 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08003985 }
Lucas Dupin60661a62018-04-12 10:50:13 -07003986 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07003987 updateBackgroundDimming();
Lucas Dupin15475702018-04-04 14:32:53 -07003988 updateAntiBurnInTranslation();
Lucas Dupinb561eda2018-04-09 17:25:04 -07003989 requestChildrenUpdate();
Selim Cinek972123d2016-05-03 14:25:58 -07003990 }
3991
Lucas Dupind285cf02018-01-18 09:18:23 -08003992 public float getDarkAmount() {
3993 return mDarkAmount;
Selim Cinek972123d2016-05-03 14:25:58 -07003994 }
3995
Lucas Dupin16cfe452018-02-08 13:14:50 -08003996 private void startBackgroundFade() {
3997 ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount,
3998 mAmbientState.isDark() ? 1f : 0);
Adrian Roos28f90c72017-05-08 17:24:26 -07003999 fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
Selim Cinek972123d2016-05-03 14:25:58 -07004000 fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
4001 fadeAnimator.start();
4002 }
4003
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004004 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07004005 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004006 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4007 }
4008 if (screenLocation.y > getBottomMostNotificationBottom()) {
4009 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
4010 }
4011 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
4012 if (child != null) {
4013 return getNotGoneIndex(child);
4014 } else {
4015 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4016 }
4017 }
4018
4019 private int getNotGoneIndex(View child) {
4020 int count = getChildCount();
4021 int notGoneIndex = 0;
4022 for (int i = 0; i < count; i++) {
4023 View v = getChildAt(i);
4024 if (child == v) {
4025 return notGoneIndex;
4026 }
4027 if (v.getVisibility() != View.GONE) {
4028 notGoneIndex++;
4029 }
4030 }
4031 return -1;
4032 }
4033
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004034 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004035 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004036 if (mFooterView != null) {
4037 index = indexOfChild(mFooterView);
4038 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004039 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004040 mFooterView = footerView;
4041 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004042 }
4043
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004044 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004045 int index = -1;
4046 if (mEmptyShadeView != null) {
4047 index = indexOfChild(mEmptyShadeView);
4048 removeView(mEmptyShadeView);
4049 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004050 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004051 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004052 }
4053
4054 public void updateEmptyShadeView(boolean visible) {
4055 int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
4056 int newVisibility = visible ? VISIBLE : GONE;
Beverlybadf6e42018-04-18 17:32:56 -04004057
4058 boolean changedVisibility = oldVisibility != newVisibility;
4059 if (changedVisibility || newVisibility != GONE) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02004060 if (newVisibility != GONE) {
Beverlybadf6e42018-04-18 17:32:56 -04004061 int oldText = mEmptyShadeView.getTextResource();
4062 int newText;
Julia Reynolds9cf17562018-03-14 09:49:35 -04004063 if (mStatusBar.areNotificationsHidden()) {
Beverlybadf6e42018-04-18 17:32:56 -04004064 newText = R.string.dnd_suppressing_shade_text;
Julia Reynolds9cf17562018-03-14 09:49:35 -04004065 } else {
Beverlybadf6e42018-04-18 17:32:56 -04004066 newText = R.string.empty_shade_text;
Julia Reynolds9cf17562018-03-14 09:49:35 -04004067 }
Beverlybadf6e42018-04-18 17:32:56 -04004068 if (changedVisibility || !Objects.equals(oldText, newText)) {
4069 mEmptyShadeView.setText(newText);
4070 showFooterView(mEmptyShadeView);
4071 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004072 } else {
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004073 hideFooterView(mEmptyShadeView, true);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004074 }
4075 }
4076 }
4077
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004078 public void updateFooterView(boolean visible, boolean showDismissView) {
4079 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004080 return;
4081 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004082 int oldVisibility = mFooterView.willBeGone() ? GONE : mFooterView.getVisibility();
Dan Sandlereceda3d2014-07-21 15:35:01 -04004083 int newVisibility = visible ? VISIBLE : GONE;
4084 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02004085 if (newVisibility != GONE) {
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004086 showFooterView(mFooterView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004087 } else {
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004088 hideFooterView(mFooterView, mFooterView.isButtonVisible());
Dan Sandlereceda3d2014-07-21 15:35:01 -04004089 }
4090 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004091 if (mFooterView.isSecondaryVisible() != showDismissView) {
4092 mFooterView.performSecondaryVisibilityAnimation(showDismissView);
4093 }
4094 }
4095
4096 private void showFooterView(StackScrollerDecorView footerView) {
4097 if (footerView.willBeGone()) {
4098 footerView.cancelAnimation();
4099 } else {
4100 footerView.setInvisible();
4101 }
4102 footerView.setVisibility(VISIBLE);
4103 footerView.setWillBeGone(false);
4104 updateContentHeight();
4105 notifyHeightChangeListener(footerView);
4106 }
4107
4108 private void hideFooterView(StackScrollerDecorView footerView, boolean isButtonVisible) {
4109 Runnable onHideFinishRunnable = new Runnable() {
4110 @Override
4111 public void run() {
4112 footerView.setVisibility(GONE);
4113 footerView.setWillBeGone(false);
4114 updateContentHeight();
4115 notifyHeightChangeListener(footerView);
4116 }
4117 };
4118 if (isButtonVisible && mIsExpanded && mAnimationsEnabled) {
4119 footerView.setWillBeGone(true);
4120 footerView.performVisibilityAnimation(false, onHideFinishRunnable);
4121 } else {
4122 onHideFinishRunnable.run();
4123 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04004124 }
4125
4126 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4127 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004128 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004129 handleDismissAllClipping();
4130 }
4131
4132 private void handleDismissAllClipping() {
4133 final int count = getChildCount();
4134 boolean previousChildWillBeDismissed = false;
4135 for (int i = 0; i < count; i++) {
4136 ExpandableView child = (ExpandableView) getChildAt(i);
4137 if (child.getVisibility() == GONE) {
4138 continue;
4139 }
4140 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4141 child.setMinClipTopAmount(child.getClipTopAmount());
4142 } else {
4143 child.setMinClipTopAmount(0);
4144 }
4145 previousChildWillBeDismissed = canChildBeDismissed(child);
4146 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004147 }
4148
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004149 public boolean isFooterViewNotGone() {
4150 return mFooterView != null
4151 && mFooterView.getVisibility() != View.GONE
4152 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004153 }
4154
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004155 public boolean isFooterViewVisible() {
4156 return mFooterView != null && mFooterView.isVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004157 }
4158
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004159 public int getFooterViewHeight() {
4160 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004161 }
4162
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004163 public int getEmptyShadeViewHeight() {
4164 return mEmptyShadeView.getHeight();
4165 }
4166
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004167 public float getBottomMostNotificationBottom() {
4168 final int count = getChildCount();
4169 float max = 0;
4170 for (int childIdx = 0; childIdx < count; childIdx++) {
4171 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4172 if (child.getVisibility() == GONE) {
4173 continue;
4174 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004175 float bottom = child.getTranslationY() + child.getActualHeight()
4176 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004177 if (bottom > max) {
4178 max = bottom;
4179 }
4180 }
Selim Cinekd2281152015-04-10 14:37:46 -07004181 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004182 }
4183
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004184 public void setStatusBar(StatusBar statusBar) {
4185 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004186 }
4187
Selim Cinekb5605e52015-02-20 18:21:41 +01004188 public void setGroupManager(NotificationGroupManager groupManager) {
4189 this.mGroupManager = groupManager;
4190 }
4191
Selim Cinekd9acca52014-09-01 22:33:25 +02004192 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01004193 requestAnimateEverything();
4194 }
4195
4196 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004197 if (mIsExpanded && mAnimationsEnabled) {
4198 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004199 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004200 requestChildrenUpdate();
4201 }
4202 }
4203
Selim Cinek04fb2582015-06-02 19:58:09 +02004204 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004205 int childCount = getChildCount();
4206 for (int i = childCount - 1; i >= 0; i--) {
4207 ExpandableView child = (ExpandableView) getChildAt(i);
4208 if (child.getVisibility() != View.GONE) {
4209 float childTop = child.getY();
4210 if (childTop > touchY) {
4211 // we are above a notification entirely let's abort
4212 return false;
4213 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004214 boolean belowChild = touchY > childTop + child.getActualHeight()
4215 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004216 if (child == mFooterView) {
4217 if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
Selim Cinekabf60bb2015-02-20 17:36:10 +01004218 touchY - childTop)) {
4219 // We clicked on the dismiss button
4220 return false;
4221 }
4222 } else if (child == mEmptyShadeView) {
4223 // We arrived at the empty shade view, for which we accept all clicks
4224 return true;
4225 } else if (!belowChild){
4226 // We are on a child
4227 return false;
4228 }
4229 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004230 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004231 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004232 }
4233
Selim Cinekb5605e52015-02-20 18:21:41 +01004234 @Override
4235 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004236 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4237 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004238 if (animated) {
4239 mExpandedGroupView = changedRow;
4240 mNeedsAnimation = true;
4241 }
4242 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004243 if (!mGroupExpandedForMeasure) {
4244 onHeightChanged(changedRow, false /* needsAnimation */);
4245 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004246 runAfterAnimationFinished(new Runnable() {
4247 @Override
4248 public void run() {
4249 changedRow.onFinishedExpansionChange();
4250 }
4251 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004252 }
4253
4254 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004255 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004256 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004257 }
4258
Selim Cinekc22fff62016-05-20 12:44:30 -07004259 /** @hide */
4260 @Override
4261 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4262 super.onInitializeAccessibilityEventInternal(event);
4263 event.setScrollable(mScrollable);
4264 event.setScrollX(mScrollX);
4265 event.setScrollY(mOwnScrollY);
4266 event.setMaxScrollX(mScrollX);
4267 event.setMaxScrollY(getScrollRange());
4268 }
4269
4270 @Override
4271 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4272 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004273 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004274 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004275 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004276 info.addAction(
4277 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4278 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4279 }
Selim Cinekef406062016-09-29 17:33:13 -07004280 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004281 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4282 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4283 }
4284 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004285 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4286 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004287 }
4288
4289 /** @hide */
4290 @Override
4291 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4292 if (super.performAccessibilityActionInternal(action, arguments)) {
4293 return true;
4294 }
4295 if (!isEnabled()) {
4296 return false;
4297 }
4298 int direction = -1;
4299 switch (action) {
4300 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4301 // fall through
4302 case android.R.id.accessibilityActionScrollDown:
4303 direction = 1;
4304 // fall through
4305 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4306 // fall through
4307 case android.R.id.accessibilityActionScrollUp:
4308 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004309 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004310 final int targetScrollY = Math.max(0,
4311 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4312 if (targetScrollY != mOwnScrollY) {
4313 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004314 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004315 return true;
4316 }
4317 break;
4318 }
4319 return false;
4320 }
4321
Selim Cinekef5127e2015-12-21 16:55:58 -08004322 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004323 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004324 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004325 }
4326
4327 public void generateChildOrderChangedEvent() {
4328 if (mIsExpanded && mAnimationsEnabled) {
4329 mGenerateChildOrderChangedEvent = true;
4330 mNeedsAnimation = true;
4331 requestChildrenUpdate();
4332 }
4333 }
4334
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004335 @Override
4336 public int getContainerChildCount() {
4337 return getChildCount();
4338 }
4339
4340 @Override
4341 public View getContainerChildAt(int i) {
4342 return getChildAt(i);
4343 }
4344
4345 @Override
4346 public void removeContainerView(View v) {
4347 removeView(v);
4348 }
4349
4350 @Override
4351 public void addContainerView(View v) {
4352 addView(v);
4353 }
4354
Selim Cinek684a4422015-04-15 16:18:39 -07004355 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004356 mAnimationFinishedRunnables.add(runnable);
4357 }
4358
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004359 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004360 mHeadsUpManager = headsUpManager;
4361 mAmbientState.setHeadsUpManager(headsUpManager);
Selim Cinek29aab962018-02-27 17:05:45 -08004362 mHeadsUpManager.addListener(mRoundnessManager);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004363 }
4364
4365 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004366 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004367 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4368 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004369 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004370 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004371 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004372 requestChildrenUpdate();
4373 }
4374 }
4375
4376 public void setShadeExpanded(boolean shadeExpanded) {
4377 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004378 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004379 }
4380
Selim Cineka59ecc32015-04-07 10:51:49 -07004381 /**
4382 * Set the boundary for the bottom heads up position. The heads up will always be above this
4383 * position.
4384 *
4385 * @param height the height of the screen
4386 * @param bottomBarHeight the height of the bar on the bottom
4387 */
4388 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4389 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4390 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004391 requestChildrenUpdate();
4392 }
4393
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004394 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
4395 mTrackingHeadsUp = row != null;
4396 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004397 }
4398
Selim Cinekaac93252015-04-14 20:04:12 -07004399 public void setScrimController(ScrimController scrimController) {
4400 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004401 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004402 }
4403
Selim Cinekbbc580b2015-06-03 14:11:03 +02004404 public void forceNoOverlappingRendering(boolean force) {
4405 mForceNoOverlappingRendering = force;
4406 }
4407
4408 @Override
4409 public boolean hasOverlappingRendering() {
4410 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4411 }
4412
Selim Cinek6811d722016-01-19 17:53:12 -08004413 public void setAnimationRunning(boolean animationRunning) {
4414 if (animationRunning != mAnimationRunning) {
4415 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004416 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004417 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004418 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004419 }
4420 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004421 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004422 }
4423 }
4424
Selim Cinek3776fe02016-02-04 13:32:43 -08004425 public boolean isExpanded() {
4426 return mIsExpanded;
4427 }
4428
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07004429 public void setPulsing(boolean pulsing, boolean animated) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004430 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004431 return;
4432 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004433 mPulsing = pulsing;
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07004434 mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
Selim Cinekebf42342017-07-13 15:46:10 +02004435 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004436 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004437 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004438 updateContentHeight();
Adrian Roosd83e9992017-03-16 15:17:57 -07004439 requestChildrenUpdate();
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07004440 notifyHeightChangeListener(null, animated);
4441 mNeedsAnimation |= animated;
4442 }
4443
4444 private void generatePulsingAnimationEvent() {
4445 if (mNeedingPulseAnimation != null) {
4446 int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
4447 : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
4448 mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
4449 mNeedingPulseAnimation = null;
4450 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004451 }
4452
Selim Cinek07304f5222016-05-19 18:31:36 -07004453 public void setFadingOut(boolean fadingOut) {
4454 if (fadingOut != mFadingOut) {
4455 mFadingOut = fadingOut;
4456 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004457 }
4458 }
4459
Selim Cinek48ff9b42016-11-09 19:31:51 -08004460 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4461 if (mScrimController == null) {
4462 // we're not set up yet.
4463 return;
4464 }
4465 if (parentNotFullyVisible != mParentNotFullyVisible) {
4466 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004467 updateFadingState();
4468 }
4469 }
4470
4471 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004472 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004473 updateSrcDrawing();
4474 }
4475
Selim Cinek31d37b92016-04-26 09:56:42 -07004476 @Override
4477 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4478 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004479 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004480 }
4481
Selim Cinekbc243a92016-09-27 16:35:13 -07004482 public void setQsExpanded(boolean qsExpanded) {
4483 mQsExpanded = qsExpanded;
4484 updateAlgorithmLayoutMinHeight();
4485 }
4486
Selim Cinekef406062016-09-29 17:33:13 -07004487 public void setOwnScrollY(int ownScrollY) {
4488 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004489 // We still want to call the normal scrolled changed for accessibility reasons
4490 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004491 mOwnScrollY = ownScrollY;
4492 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004493 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004494 }
4495 }
4496
Selim Cinek281c2022016-10-13 19:14:43 -07004497 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004498 int index = -1;
4499 if (mShelf != null) {
4500 index = indexOfChild(mShelf);
4501 removeView(mShelf);
4502 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004503 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004504 addView(mShelf, index);
4505 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004506 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004507 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004508 }
4509
4510 public NotificationShelf getNotificationShelf() {
4511 return mShelf;
4512 }
4513
Selim Cinekad7fac02016-10-18 17:09:15 -07004514 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4515 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4516 mMaxDisplayedNotifications = maxDisplayedNotifications;
4517 updateContentHeight();
4518 notifyHeightChangeListener(mShelf);
4519 }
4520 }
4521
Selim Cinek48ff9b42016-11-09 19:31:51 -08004522 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004523 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004524 }
4525
Selim Cinekcafa87f2016-10-26 17:00:17 -07004526 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4527 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4528 updateClipping();
4529 }
4530
4531 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4532 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4533 updateClipping();
4534 }
4535
Selim Cinek355652a2016-12-07 13:32:12 -08004536 public void setStatusBarState(int statusBarState) {
4537 mStatusBarState = statusBarState;
4538 mAmbientState.setStatusBarState(statusBarState);
4539 }
4540
Selim Cinekd5ab6452016-12-08 16:34:00 -08004541 public void setExpandingVelocity(float expandingVelocity) {
4542 mAmbientState.setExpandingVelocity(expandingVelocity);
4543 }
4544
Selim Cinekfcff4c62016-12-27 14:26:06 +01004545 public float getOpeningHeight() {
4546 if (mEmptyShadeView.getVisibility() == GONE) {
4547 return getMinExpansionHeight();
4548 } else {
4549 return getAppearEndPosition();
4550 }
4551 }
4552
4553 public void setIsFullWidth(boolean isFullWidth) {
4554 mAmbientState.setPanelFullWidth(isFullWidth);
4555 }
4556
Selim Cinekec29d342017-05-05 18:31:49 -07004557 public void setUnlockHintRunning(boolean running) {
4558 mAmbientState.setUnlockHintRunning(running);
4559 }
4560
Selim Cinek5cf1d052017-06-01 17:36:46 -07004561 public void setQsCustomizerShowing(boolean isShowing) {
4562 mAmbientState.setQsCustomizerShowing(isShowing);
4563 requestChildrenUpdate();
4564 }
4565
4566 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4567 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4568 }
4569
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004570 public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
4571 mAntiBurnInOffsetX = antiBurnInOffsetX;
4572 updateAntiBurnInTranslation();
Adrian Roosdc747bd2017-06-01 16:09:15 -07004573 }
4574
Selim Cinek707e2072017-06-30 18:32:40 +02004575 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4576 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
4577 + " alpha:%f scrollY:%d]",
4578 this.getClass().getSimpleName(),
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004579 mPulsing ? "T":"f",
Selim Cinek707e2072017-06-30 18:32:40 +02004580 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4581 getVisibility() == View.VISIBLE ? "visible"
4582 : getVisibility() == View.GONE ? "gone"
4583 : "invisible",
4584 getAlpha(),
4585 mAmbientState.getScrollY()));
4586 }
4587
Lucas Dupin16cfe452018-02-08 13:14:50 -08004588 public boolean isFullyDark() {
4589 return mAmbientState.isFullyDark();
4590 }
4591
Selim Cinek7103fd42016-05-09 22:22:33 -04004592 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004593 * Add a listener whenever the expanded height changes. The first value passed as an argument
4594 * is the expanded height and the second one is the appearFraction.
4595 *
4596 * @param listener the listener to notify.
4597 */
4598 public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4599 mExpandedHeightListeners.add(listener);
4600 }
4601
4602 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07004603 * Stop a listener from listening to the expandedHeight.
4604 */
4605 public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4606 mExpandedHeightListeners.remove(listener);
4607 }
4608
4609 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004610 * A listener that is notified when the empty space below the notifications is clicked on
4611 */
4612 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004613 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004614 }
4615
4616 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004617 * A listener that gets notified when the overscroll at the top has changed.
4618 */
4619 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004620
4621 /**
4622 * Notifies a listener that the overscroll has changed.
4623 *
4624 * @param amount the amount of overscroll, in pixels
4625 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4626 * unrubberbanded motion to directly expand overscroll view (e.g expand
4627 * QS)
4628 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004629 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004630
4631 /**
4632 * Notify a listener that the scroller wants to escape from the scrolling motion and
4633 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4634 *
4635 * @param velocity The velocity that the Scroller had when over flinging
4636 * @param open Should the fling open or close the overscroll view.
4637 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004638 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004639 }
4640
Mady Mellor95d743c2017-01-10 12:05:27 -08004641 private class NotificationSwipeHelper extends SwipeHelper
4642 implements NotificationSwipeActionHelper {
4643 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004644 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004645 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004646
4647 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4648 super(swipeDirection, callback, context);
4649 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004650 mFalsingCheck = new Runnable() {
4651 @Override
4652 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004653 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004654 }
4655 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004656 }
4657
4658 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004659 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004660 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004661 if (mCurrMenuRow != null) {
4662 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004663 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004664 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004665 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004666
Mady Mellor95d743c2017-01-10 12:05:27 -08004667 // Slide back any notifications that might be showing a menu
4668 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004669
4670 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004671 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
4672 mCurrMenuRow = row.createMenu();
4673 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4674 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor89e15ec2017-06-28 17:08:21 -07004675 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004676 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004677 }
4678
4679 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004680 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004681 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004682 if (mCurrMenuRow != null) {
4683 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004684 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004685 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004686
Mady Mellor95d743c2017-01-10 12:05:27 -08004687 @Override
4688 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4689 float translation) {
4690 if (mCurrMenuRow != null) {
4691 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004692 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004693 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004694 }
4695
4696 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004697 public void dismissChild(final View view, float velocity,
4698 boolean useAccelerateInterpolator) {
4699 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004700 if (mIsExpanded) {
4701 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4702 // of the panel early.
Rohan Shah524cf7b2018-03-15 14:40:02 -07004703 handleChildViewDismissed(view);
Selim Cinekd1395642016-04-28 12:22:42 -07004704 }
Eliot Courtney47098cb2017-10-18 17:30:30 +09004705 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
4706 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4707 false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004708 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004709 }
4710
4711 @Override
4712 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004713 super.snapChild(animView, targetLeft, velocity);
4714 onDragCancelled(animView);
4715 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004716 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004717 }
4718 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004719
4720 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004721 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4722 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004723 }
4724
Mady Mellorbd707492017-05-10 17:51:25 -07004725 public boolean isFalseGesture(MotionEvent ev) {
4726 return super.isFalseGesture(ev);
4727 }
4728
Mady Mellor95d743c2017-01-10 12:05:27 -08004729 private void handleMenuCoveredOrDismissed() {
4730 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4731 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004732 }
4733 }
4734
Mady Mellor4b80b102016-01-22 08:03:58 -08004735 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004736 public Animator getViewTranslationAnimator(View v, float target,
4737 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004738 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004739 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4740 } else {
4741 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004742 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004743 }
4744
4745 @Override
4746 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004747 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004748 }
4749
4750 @Override
4751 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004752 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004753 }
4754
Mady Mellor95d743c2017-01-10 12:05:27 -08004755 @Override
4756 public void dismiss(View animView, float velocity) {
4757 dismissChild(animView, velocity,
4758 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4759 }
4760
4761 @Override
4762 public void snap(View animView, float targetLeft, float velocity) {
4763 snapChild(animView, targetLeft, velocity);
4764 }
4765
4766 @Override
4767 public boolean swipedFarEnough(float translation, float viewSize) {
4768 return swipedFarEnough();
4769 }
4770
4771 @Override
4772 public boolean swipedFastEnough(float translation, float velocity) {
4773 return swipedFastEnough();
4774 }
4775
4776 @Override
4777 public float getMinDismissVelocity() {
4778 return getEscapeVelocity();
4779 }
4780
4781 public void onMenuShown(View animView) {
4782 onDragCancelled(animView);
4783
4784 // If we're on the lockscreen we want to false this.
4785 if (isAntiFalsingNeeded()) {
4786 mHandler.removeCallbacks(mFalsingCheck);
4787 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4788 }
4789 }
4790
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004791 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09004792 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004793 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004794 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4795 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004796 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004797 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004798 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004799 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004800 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004801 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004802 if (view != null && !isTouchInView(ev, view)) {
4803 // Touch was outside visible guts / menu notification, close what's visible
Eliot Courtney47098cb2017-10-18 17:30:30 +09004804 mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
4805 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4806 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004807 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004808 }
4809 }
4810
Mady Mellor95d743c2017-01-10 12:05:27 -08004811 public void resetExposedMenuView(boolean animate, boolean force) {
4812 if (mMenuExposedView == null
4813 || (!force && mMenuExposedView == mTranslatingParentView)) {
4814 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004815 return;
4816 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004817 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004818 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004819 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004820 0 /* leftTarget */, null /* updateListener */);
4821 if (anim != null) {
4822 anim.start();
4823 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004824 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
Selim Cinekb2e0f332017-08-18 12:24:38 -07004825 ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
4826 if (!row.isRemoved()) {
4827 row.resetTranslation();
4828 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004829 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004830 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004831 }
4832 }
4833
Mady Mellorc2dbe492017-03-30 13:22:03 -07004834 private boolean isTouchInView(MotionEvent ev, View view) {
4835 if (view == null) {
4836 return false;
4837 }
4838 final int height = (view instanceof ExpandableView)
4839 ? ((ExpandableView) view).getActualHeight()
4840 : view.getHeight();
4841 final int rx = (int) ev.getRawX();
4842 final int ry = (int) ev.getRawY();
4843 view.getLocationOnScreen(mTempInt2);
4844 final int x = mTempInt2[0];
4845 final int y = mTempInt2[1];
4846 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4847 boolean ret = rect.contains(rx, ry);
4848 return ret;
4849 }
4850
Selim Cinek33223572016-02-19 19:32:22 -08004851 private void updateContinuousShadowDrawing() {
4852 boolean continuousShadowUpdate = mAnimationRunning
4853 || !mAmbientState.getDraggedViews().isEmpty();
4854 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4855 if (continuousShadowUpdate) {
4856 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4857 } else {
4858 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4859 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004860 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004861 }
4862 }
4863
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004864 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004865 public void resetExposedMenuView(boolean animate, boolean force) {
4866 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004867 }
4868
4869 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4870 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4871 }
4872
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004873 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004874
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004875 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4876
4877 // ANIMATION_TYPE_ADD
4878 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004879 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004880 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004881 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004882 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004883 .animateZ()
4884 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004885
4886 // ANIMATION_TYPE_REMOVE
4887 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004888 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004889 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004890 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004891 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004892 .animateZ()
4893 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004894
4895 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4896 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004897 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004898 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004899 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004900 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004901 .animateZ()
4902 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004903
4904 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4905 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004906 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004907 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004908 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004909 .animateY()
4910 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004911 .animateZ(),
4912
4913 // ANIMATION_TYPE_START_DRAG
4914 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004915 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004916
4917 // ANIMATION_TYPE_SNAP_BACK
4918 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004919 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004920 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004921
4922 // ANIMATION_TYPE_ACTIVATED_CHILD
4923 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004924 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004925
4926 // ANIMATION_TYPE_DIMMED
4927 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004928 .animateDimmed(),
4929
4930 // ANIMATION_TYPE_CHANGE_POSITION
4931 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004932 .animateAlpha() // maybe the children change positions
4933 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004934 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004935 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004936 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004937 .animateZ(),
4938
4939 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004940 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004941
4942 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4943 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004944 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004945 .animateHeight()
4946 .animateTopInset()
4947 .animateY()
4948 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004949 .animateZ()
4950 .hasDelays(),
4951
4952 // ANIMATION_TYPE_HIDE_SENSITIVE
4953 new AnimationFilter()
4954 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02004955
4956 // ANIMATION_TYPE_VIEW_RESIZE
4957 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004958 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02004959 .animateHeight()
4960 .animateTopInset()
4961 .animateY()
4962 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02004963
Selim Cinekb5605e52015-02-20 18:21:41 +01004964 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
4965 new AnimationFilter()
4966 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004967 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01004968 .animateHeight()
4969 .animateTopInset()
4970 .animateY()
4971 .animateZ(),
4972
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004973 // ANIMATION_TYPE_HEADS_UP_APPEAR
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()
4979 .animateZ(),
4980
4981 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4982 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004983 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004984 .animateHeight()
4985 .animateTopInset()
4986 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07004987 .animateZ()
4988 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004989
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004990 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4991 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004992 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004993 .animateHeight()
4994 .animateTopInset()
4995 .animateY()
4996 .animateZ()
4997 .hasDelays(),
4998
Selim Cineka59ecc32015-04-07 10:51:49 -07004999 // ANIMATION_TYPE_HEADS_UP_OTHER
5000 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005001 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07005002 .animateHeight()
5003 .animateTopInset()
5004 .animateY()
5005 .animateZ(),
5006
Selim Cinekd9acca52014-09-01 22:33:25 +02005007 // ANIMATION_TYPE_EVERYTHING
5008 new AnimationFilter()
5009 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005010 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02005011 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02005012 .animateDimmed()
5013 .animateHideSensitive()
5014 .animateHeight()
5015 .animateTopInset()
5016 .animateY()
5017 .animateZ(),
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005018
5019 // ANIMATION_TYPE_PULSE_APPEAR
5020 new AnimationFilter()
5021 .animateAlpha()
5022 .animateY(),
5023
5024 // ANIMATION_TYPE_PULSE_DISAPPEAR
5025 new AnimationFilter()
5026 .animateAlpha()
5027 .animateY(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005028 };
5029
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005030 static int[] LENGTHS = new int[] {
5031
5032 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005033 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005034
5035 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005036 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005037
5038 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5039 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5040
5041 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5042 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5043
5044 // ANIMATION_TYPE_START_DRAG
5045 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5046
5047 // ANIMATION_TYPE_SNAP_BACK
5048 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5049
5050 // ANIMATION_TYPE_ACTIVATED_CHILD
5051 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
5052
5053 // ANIMATION_TYPE_DIMMED
5054 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005055
5056 // ANIMATION_TYPE_CHANGE_POSITION
5057 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04005058
5059 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07005060 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005061
5062 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5063 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02005064
5065 // ANIMATION_TYPE_HIDE_SENSITIVE
5066 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02005067
5068 // ANIMATION_TYPE_VIEW_RESIZE
5069 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02005070
Selim Cinekb5605e52015-02-20 18:21:41 +01005071 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08005072 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01005073
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005074 // ANIMATION_TYPE_HEADS_UP_APPEAR
5075 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
5076
5077 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5078 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5079
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005080 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5081 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5082
Selim Cineka59ecc32015-04-07 10:51:49 -07005083 // ANIMATION_TYPE_HEADS_UP_OTHER
5084 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5085
Selim Cinekd9acca52014-09-01 22:33:25 +02005086 // ANIMATION_TYPE_EVERYTHING
5087 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005088
5089 // ANIMATION_TYPE_PULSE_APPEAR
5090 KeyguardSliceView.DEFAULT_ANIM_DURATION,
5091
5092 // ANIMATION_TYPE_PULSE_DISAPPEAR
5093 KeyguardSliceView.DEFAULT_ANIM_DURATION / 2,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005094 };
5095
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005096 static final int ANIMATION_TYPE_ADD = 0;
5097 static final int ANIMATION_TYPE_REMOVE = 1;
5098 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
5099 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
5100 static final int ANIMATION_TYPE_START_DRAG = 4;
5101 static final int ANIMATION_TYPE_SNAP_BACK = 5;
5102 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
5103 static final int ANIMATION_TYPE_DIMMED = 7;
5104 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04005105 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005106 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02005107 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02005108 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01005109 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005110 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
5111 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005112 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
5113 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
5114 static final int ANIMATION_TYPE_EVERYTHING = 18;
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005115 static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
5116 static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005117
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005118 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
5119 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
5120
Selim Cinek572bbd42014-04-25 16:43:27 +02005121 final long eventStartTime;
5122 final View changingView;
5123 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005124 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005125 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005126 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005127 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07005128 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02005129
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005130 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005131 this(view, type, LENGTHS[type]);
5132 }
5133
Adrian Roos28f90c72017-05-08 17:24:26 -07005134 AnimationEvent(View view, int type, AnimationFilter filter) {
5135 this(view, type, LENGTHS[type], filter);
5136 }
5137
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005138 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07005139 this(view, type, length, FILTERS[type]);
5140 }
5141
5142 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02005143 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
5144 changingView = view;
5145 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005146 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07005147 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005148 }
5149
5150 /**
5151 * Combines the length of several animation events into a single value.
5152 *
5153 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005154 * @return The combined length. Depending on the event types, this might be the maximum of
5155 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005156 */
5157 static long combineLength(ArrayList<AnimationEvent> events) {
5158 long length = 0;
5159 int size = events.size();
5160 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005161 AnimationEvent event = events.get(i);
5162 length = Math.max(length, event.length);
5163 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
5164 return event.length;
5165 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005166 }
5167 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02005168 }
5169 }
Selim Cinek67b22602014-03-10 15:40:16 +01005170}