blob: 4c241bd18c3cbf69a9b905170ab0de3f6179f812 [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;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010037import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080038import android.graphics.PorterDuff;
39import android.graphics.PorterDuffXfermode;
40import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070041import android.os.Bundle;
Mady Mellor4b80b102016-01-22 08:03:58 -080042import android.os.Handler;
Mady Mellor95d743c2017-01-10 12:05:27 -080043import android.service.notification.StatusBarNotification;
Aurimas Liutikasd3667712018-04-17 09:50:46 -070044import androidx.annotation.NonNull;
45import androidx.annotation.VisibleForTesting;
46import androidx.core.graphics.ColorUtils;
Selim Cinek67b22602014-03-10 15:40:16 +010047import android.util.AttributeSet;
48import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080049import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070050import android.util.Pair;
Lucas Dupine17ce522017-07-17 15:45:06 -070051import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070052import android.view.InputDevice;
Selim Cinek67b22602014-03-10 15:40:16 +010053import android.view.MotionEvent;
54import android.view.VelocityTracker;
55import android.view.View;
56import android.view.ViewConfiguration;
57import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020058import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070059import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070060import android.view.accessibility.AccessibilityEvent;
61import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020062import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080063import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010064import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070065import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070066
Mady Mellora41587b2016-02-11 18:43:06 -080067import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010068import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -070069import com.android.keyguard.KeyguardSliceView;
Lucas Dupine17ce522017-07-17 15:45:06 -070070import com.android.settingslib.Utils;
Rohan Shah524cf7b2018-03-15 14:40:02 -070071import com.android.systemui.Dependency;
Selim Cinek67b22602014-03-10 15:40:16 +010072import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080073import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010074import com.android.systemui.R;
75import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070076import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080077import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
78import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
79import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Selim Cineka32ab602014-06-11 15:06:01 +020080import com.android.systemui.statusbar.ActivatableNotificationView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020081import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010082import com.android.systemui.statusbar.ExpandableNotificationRow;
Jorim Jaggibe565df2014-04-28 17:51:23 +020083import com.android.systemui.statusbar.ExpandableView;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -040084import com.android.systemui.statusbar.FooterView;
Rohan Shah524cf7b2018-03-15 14:40:02 -070085import com.android.systemui.statusbar.NotificationBlockingHelperManager;
Adrian Roos7d062c42017-03-30 15:11:43 -070086import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070087import com.android.systemui.statusbar.NotificationGuts;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090088import com.android.systemui.statusbar.NotificationListContainer;
89import com.android.systemui.statusbar.NotificationLogger;
Selim Cinek0cfbef42016-11-09 19:06:36 -080090import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070091import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010092import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020093import com.android.systemui.statusbar.StatusBarState;
Selim Cinek33223572016-02-19 19:32:22 -080094import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080095import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080096import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Lucas Dupinb561eda2018-04-09 17:25:04 -070097import com.android.systemui.statusbar.phone.DozeParameters;
Selim Cinekf0c79e12018-05-14 17:17:31 -070098import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090099import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Selim Cinekb5605e52015-02-20 18:21:41 +0100100import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700101import com.android.systemui.statusbar.phone.NotificationIconAreaController;
Selim Cinekaac93252015-04-14 20:04:12 -0700102import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800103import com.android.systemui.statusbar.phone.StatusBar;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900104import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100105import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +0100106
Selim Cinek707e2072017-06-30 18:32:40 +0200107import java.io.FileDescriptor;
108import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200109import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800110import java.util.Collections;
111import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200112import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800113import java.util.List;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800114import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200115
Selim Cinek67b22602014-03-10 15:40:16 +0100116/**
117 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
118 */
119public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200120 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800121 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900122 NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
123 NotificationListContainer {
Selim Cinek67b22602014-03-10 15:40:16 +0100124
Selim Cinekd35c2792016-01-21 13:20:57 -0800125 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800126 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100127 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200128 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
129 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200130 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100131 /**
132 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
133 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800134 private static final int INVALID_POINTER = -1;
Selim Cinek67b22602014-03-10 15:40:16 +0100135
Selim Cinek1408eb52014-06-02 14:45:38 +0200136 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800137 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200138 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100139 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800140 private final Paint mBackgroundPaint = new Paint();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700141 private final boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100142
Selim Cinekbc243a92016-09-27 16:35:13 -0700143 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100144 private int mOwnScrollY;
145 private int mMaxLayoutHeight;
146
147 private VelocityTracker mVelocityTracker;
148 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100149 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100150 private int mTouchSlop;
151 private int mMinimumVelocity;
152 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100153 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200154 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100155 private boolean mIsBeingDragged;
156 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200157 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700158 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100159 private boolean mTouchIsClick;
160 private float mInitialTouchX;
161 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100162
Selim Cinek67b22602014-03-10 15:40:16 +0100163 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100164 private int mContentHeight;
Lucas Dupin60661a62018-04-12 10:50:13 -0700165 private int mIntrinsicContentHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100166 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100167 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800168 private int mIncreasedPaddingBetweenElements;
shawnlin8e4e92c2018-04-12 18:47:24 +0800169 private int mMaxTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800170 private int mRegularTopPadding;
171 private int mDarkTopPadding;
172 // Current padding, will be either mRegularTopPadding or mDarkTopPadding
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200173 private int mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800174 // Distance between AOD separator and shelf
175 private int mDarkSeparatorPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700176 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700177 private int mBottomInset = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +0800178 private float mQsExpansionFraction;
Selim Cinek67b22602014-03-10 15:40:16 +0100179
180 /**
181 * The algorithm which calculates the properties for our children
182 */
Muyuan Li87798022016-04-07 17:51:25 -0700183 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100184
185 /**
186 * The current State this Layout is in
187 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200188 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700189 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100190 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800191 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700192 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
193 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
194 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
195 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
196 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200197 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700198 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
199 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200200 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200201 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200202 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800203 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200204
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200205 /**
206 * The raw amount of the overScroll on the top, which is not rubber-banded.
207 */
208 private float mOverScrolledTopPixels;
209
210 /**
211 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
212 */
213 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900214 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200215 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200216 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100217 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200218 private boolean mNeedsAnimation;
219 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200220 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200221 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400222 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100223 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200224 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200225 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200226 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200227 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200228 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700229 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200230 private boolean mExpandingNotification;
231 private boolean mExpandedInThisMotion;
shawnlin8e4e92c2018-04-12 18:47:24 +0800232 private boolean mShouldShowShelfOnly;
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 Cinek9dd0d042018-05-14 18:12:42 -0700293 private HashSet<ExpandableView> mClearTransientViewsWhenFinished = 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;
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200375
376 /**
377 * @see #setDarkAmount(float, float)
378 */
379 private float mInterpolatedDarkAmount = 0f;
380
381 /**
382 * @see #setDarkAmount(float, float)
383 */
384 private float mLinearDarkAmount = 0f;
Lucas Dupin439bd442018-06-12 15:05:28 -0700385
386 /**
387 * How fast the background scales in the X direction as a factor of the Y expansion.
388 */
389 private float mBackgroundXFactor = 1f;
Selim Cinek972123d2016-05-03 14:25:58 -0700390
Lucas Dupine17ce522017-07-17 15:45:06 -0700391 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700392 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700393 private boolean mForwardScrollable;
394 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700395 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700396 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800397 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700398 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800399 private boolean mNoAmbient;
400 private final Rect mClipRect = new Rect();
401 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700402 private Rect mRequestedClipBounds;
403 private boolean mInHeadsUpPinnedMode;
404 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800405 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100406 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700407 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800408 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek0fe07392017-11-09 13:26:34 -0800409 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800410 private int mSidePaddings;
Lucas Dupind285cf02018-01-18 09:18:23 -0800411 private final int mSeparatorWidth;
412 private final int mSeparatorThickness;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800413 private final Rect mBackgroundAnimationRect = new Rect();
Lucas Dupin0cd882f2018-01-30 12:19:49 -0800414 private int mAntiBurnInOffsetX;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800415 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
416 private int mHeadsUpInset;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700417 private HeadsUpAppearanceController mHeadsUpAppearanceController;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700418 private NotificationIconAreaController mIconAreaController;
Bill Lin278e7d62018-06-13 18:07:15 +0800419 private float mVerticalPanelTranslation;
Selim Cinek67b22602014-03-10 15:40:16 +0100420
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200421 private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
422
Selim Cinek67b22602014-03-10 15:40:16 +0100423 public NotificationStackScrollLayout(Context context) {
424 this(context, null);
425 }
426
427 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
428 this(context, attrs, 0);
429 }
430
431 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
432 this(context, attrs, defStyleAttr, 0);
433 }
434
435 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
436 int defStyleRes) {
437 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800438 Resources res = getResources();
439
Selim Cinek281c2022016-10-13 19:14:43 -0700440 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800441 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800442 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
443 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200444 mExpandHelper = new ExpandHelper(getContext(), this,
445 minHeight, maxHeight);
446 mExpandHelper.setEventSource(this);
447 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800448 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Muyuan Li333a4fc2016-04-16 17:13:46 -0700449 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100450 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800451 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800452 mShouldDrawNotificationBackground =
453 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700454 mFadeNotificationsOnDismiss =
455 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Lucas Dupind285cf02018-01-18 09:18:23 -0800456 mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
457 mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
458 mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
Selim Cinek29aab962018-02-27 17:05:45 -0800459 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
460 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800461 addOnExpandedHeightListener(mRoundnessManager::setExpanded);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800462
Rohan Shah524cf7b2018-03-15 14:40:02 -0700463 // Blocking helper manager wants to know the expanded state, update as well.
464 NotificationBlockingHelperManager blockingHelperManager =
465 Dependency.get(NotificationBlockingHelperManager.class);
466 addOnExpandedHeightListener((height, unused) -> {
467 blockingHelperManager.setNotificationShadeExpanded(height);
468 });
469
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800470 updateWillNotDraw();
Lucas Dupind285cf02018-01-18 09:18:23 -0800471 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100472 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100473 mDebugPaint = new Paint();
474 mDebugPaint.setColor(0xffff0000);
475 mDebugPaint.setStrokeWidth(2);
476 mDebugPaint.setStyle(Paint.Style.STROKE);
477 }
478 }
479
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900480 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800481 public NotificationSwipeActionHelper getSwipeActionHelper() {
482 return mSwipeHelper;
483 }
484
Selim Cinek67b22602014-03-10 15:40:16 +0100485 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800486 public void onMenuClicked(View view, int x, int y, MenuItem item) {
487 if (mLongPressListener == null) {
488 return;
489 }
490 if (view instanceof ExpandableNotificationRow) {
491 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800492 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
493 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800494 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800495 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800496 }
497
498 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800499 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700500 if (mTranslatingParentView != null && row == mTranslatingParentView) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800501 mMenuExposedView = null;
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700502 mTranslatingParentView = null;
503 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000504 }
505
506 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800507 public void onMenuShown(View row) {
508 mMenuExposedView = mTranslatingParentView;
509 if (row instanceof ExpandableNotificationRow) {
510 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
511 ((ExpandableNotificationRow) row).getStatusBarNotification()
512 .getPackageName());
513 }
514 mSwipeHelper.onMenuShown(row);
515 }
516
Selim Cinek67b22602014-03-10 15:40:16 +0100517 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800518 if (mShouldDrawNotificationBackground
519 && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
520 drawBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700521 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800522
Selim Cinek67b22602014-03-10 15:40:16 +0100523 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800524 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100525 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800526 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100527 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200528 y = getHeight() - getEmptyBottomMargin();
529 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100530 }
531 }
532
Lucas Dupind285cf02018-01-18 09:18:23 -0800533 private void drawBackground(Canvas canvas) {
534 final int lockScreenLeft = mSidePaddings;
535 final int lockScreenRight = getWidth() - mSidePaddings;
536 final int lockScreenTop = mCurrentBounds.top;
537 final int lockScreenBottom = mCurrentBounds.bottom;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700538 int separatorWidth = 0;
539 int separatorThickness = 0;
540 if (mIconAreaController.hasShelfIconsWhenFullyDark()) {
541 separatorThickness = mSeparatorThickness;
542 separatorWidth = mSeparatorWidth;
543 }
544 final int darkLeft = getWidth() / 2 - separatorWidth / 2;
545 final int darkRight = darkLeft + separatorWidth;
546 final int darkTop = (int) (mRegularTopPadding + separatorThickness / 2f);
547 final int darkBottom = darkTop + separatorThickness;
Lucas Dupind285cf02018-01-18 09:18:23 -0800548
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800549 if (mAmbientState.hasPulsingNotifications()) {
Lucas Dupin16cfe452018-02-08 13:14:50 -0800550 // No divider, we have a notification icon instead
551 } else if (mAmbientState.isFullyDark()) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800552 // Only draw divider on AOD if we actually have notifications
553 if (mFirstVisibleBackgroundChild != null) {
554 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
555 }
Lucas Dupind285cf02018-01-18 09:18:23 -0800556 } else {
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200557 float yProgress = 1 - mInterpolatedDarkAmount;
558 float xProgress = mDarkXInterpolator.getInterpolation(
559 (1 - mLinearDarkAmount) * mBackgroundXFactor);
Lucas Dupin60661a62018-04-12 10:50:13 -0700560
Lucas Dupin16cfe452018-02-08 13:14:50 -0800561 mBackgroundAnimationRect.set(
Lucas Dupin60661a62018-04-12 10:50:13 -0700562 (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
563 (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
564 (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
565 (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200566
Lucas Dupin16cfe452018-02-08 13:14:50 -0800567 if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
568 canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
569 mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
570 mCornerRadius, mCornerRadius, mBackgroundPaint);
571 }
Lucas Dupind285cf02018-01-18 09:18:23 -0800572 }
Lucas Dupin16cfe452018-02-08 13:14:50 -0800573 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800574 }
575
Selim Cinekd35c2792016-01-21 13:20:57 -0800576 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800577 // No need to update the background color if it's not being drawn.
578 if (!mShouldDrawNotificationBackground) {
579 return;
580 }
581
Lucas Dupinb561eda2018-04-09 17:25:04 -0700582 float alpha =
583 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200584 alpha *= 1f - mInterpolatedDarkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -0700585 // We need to manually blend in the background color.
586 int scrimColor = mScrimController.getBackgroundColor();
587 int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
588
589 // Interpolate between semi-transparent notification panel background color
590 // and white AOD separator.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200591 float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(
592 mInterpolatedDarkAmount);
Lucas Dupinb561eda2018-04-09 17:25:04 -0700593 int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800594
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100595 if (mCachedBackgroundColor != color) {
596 mCachedBackgroundColor = color;
597 mBackgroundPaint.setColor(color);
598 invalidate();
599 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800600 }
601
Selim Cinek67b22602014-03-10 15:40:16 +0100602 private void initView(Context context) {
603 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100604 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200605 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100606 final ViewConfiguration configuration = ViewConfiguration.get(context);
607 mTouchSlop = configuration.getScaledTouchSlop();
608 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
609 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100610 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700611
612 Resources res = context.getResources();
613 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800614 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700615 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700616 mPaddingBetweenElements = Math.max(1,
617 res.getDimensionPixelSize(R.dimen.notification_divider_height));
618 mIncreasedPaddingBetweenElements =
619 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
620 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200621 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800622 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700623 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800624 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700625 mMinInteractionHeight = res.getDimensionPixelSize(
626 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800627 mCornerRadius = res.getDimensionPixelSize(
628 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800629 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
630 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200631 }
632
Selim Cinek25503252016-03-03 15:31:43 -0800633 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700634 mDrawBackgroundAsSrc = asSrc;
635 updateSrcDrawing();
636 }
637
638 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800639 if (!mShouldDrawNotificationBackground) {
640 return;
641 }
642
Selim Cinek48ff9b42016-11-09 19:31:51 -0800643 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700644 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800645 invalidate();
646 }
647
Selim Cinekaef92ef2014-06-06 18:06:04 +0200648 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700649 notifyHeightChangeListener(view, false /* needsAnimation */);
650 }
651
652 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +0200653 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700654 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200655 }
Selim Cinek67b22602014-03-10 15:40:16 +0100656 }
657
658 @Override
659 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
660 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -0800661
662 int width = MeasureSpec.getSize(widthMeasureSpec);
663 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
664 MeasureSpec.getMode(widthMeasureSpec));
Selim Cinekfa760d42016-05-10 15:50:53 -0400665 // We need to measure all children even the GONE ones, such that the heights are calculated
666 // correctly as they are used to calculate how many we can fit on the screen.
667 final int size = getChildCount();
668 for (int i = 0; i < size; i++) {
Selim Cinekb95fd182017-12-21 13:03:32 -0800669 measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400670 }
Selim Cinek67b22602014-03-10 15:40:16 +0100671 }
672
673 @Override
674 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100675 // we layout all our children centered on the top
676 float centerX = getWidth() / 2.0f;
677 for (int i = 0; i < getChildCount(); i++) {
678 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400679 // We need to layout all children even the GONE ones, such that the heights are
680 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100681 float width = child.getMeasuredWidth();
682 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100683 child.layout((int) (centerX - width / 2.0f),
684 0,
685 (int) (centerX + width / 2.0f),
686 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100687 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200688 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100689 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200690 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200691 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800692 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700693 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100694 }
695
Selim Cinek5bc852a2015-12-21 12:19:09 -0800696 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
697 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200698 mNeedViewResizeAnimation = true;
699 mNeedsAnimation = true;
700 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200701 }
702
Selim Cinekdb167372016-11-17 15:41:17 -0800703 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
704 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800705 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200706 }
707
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900708 @Override
709 public void setChildLocationsChangedListener(
710 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200711 mListener = listener;
712 }
713
Selim Cineka7d4f822016-12-06 14:34:47 -0800714 @Override
715 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
716 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200717 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800718 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200719 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100720 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800721 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100722 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800723 if (row.getVisibility() != View.VISIBLE) {
724 return false;
725 }
726 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200727 }
728
Selim Cinek67b22602014-03-10 15:40:16 +0100729 private void setMaxLayoutHeight(int maxLayoutHeight) {
730 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700731 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200732 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100733 }
734
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200735 private void updateAlgorithmHeightAndPadding() {
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200736 mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding,
737 mInterpolatedDarkAmount);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700738 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700739 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700740 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100741 }
742
Selim Cinekbc243a92016-09-27 16:35:13 -0700743 private void updateAlgorithmLayoutMinHeight() {
shawnlinc3457912018-05-15 16:39:56 +0800744 mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800745 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -0700746 }
747
Selim Cinek67b22602014-03-10 15:40:16 +0100748 /**
749 * Updates the children views according to the stack scroll algorithm. Call this whenever
750 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
751 */
752 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800753 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800754 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
755 ? 0
756 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200757 mAmbientState.setScrollY(mOwnScrollY);
758 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200759 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200760 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100761 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200762 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100763 }
764 }
765
Selim Cinekc383fd02016-10-21 15:31:26 -0700766 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700767 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800768 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -0700769 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
770 updateBackground();
771 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700772 }
773
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800774 private void updateClippingToTopRoundedCorner() {
Arthur Hungc0ef5652018-05-22 14:00:42 +0800775 Float clipStart = (float) mTopPadding
776 + mStackTranslation
777 + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800778 Float clipEnd = clipStart + mCornerRadius;
779 boolean first = true;
780 for (int i = 0; i < getChildCount(); i++) {
781 ExpandableView child = (ExpandableView) getChildAt(i);
782 if (child.getVisibility() == GONE) {
783 continue;
784 }
785 float start = child.getTranslationY();
Arthur Hungc0ef5652018-05-22 14:00:42 +0800786 float end = start + child.getActualHeight();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800787 boolean clip = clipStart > start && clipStart < end
788 || clipEnd >= start && clipEnd <= end;
789 clip &= !(first && mOwnScrollY == 0);
Selim Cinekeccf4942018-05-30 09:55:36 -0700790 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
791 : ExpandableView.NO_ROUNDNESS);
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800792 first = false;
793 }
794 }
795
Selim Cinek3776fe02016-02-04 13:32:43 -0800796 private void updateScrollStateForAddedChildren() {
797 if (mChildrenToAddAnimated.isEmpty()) {
798 return;
799 }
800 for (int i = 0; i < getChildCount(); i++) {
801 ExpandableView child = (ExpandableView) getChildAt(i);
802 if (mChildrenToAddAnimated.contains(child)) {
803 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800804 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
805 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
806 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800807 int childHeight = getIntrinsicHeight(child) + padding;
808 if (startingPosition < mOwnScrollY) {
809 // This child starts off screen, so let's keep it offscreen to keep the others visible
810
Selim Cinekef406062016-09-29 17:33:13 -0700811 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800812 }
813 }
814 }
815 clampScrollPosition();
816 }
817
Adrian Roos181385c2016-05-05 17:45:44 -0400818 private void updateForcedScroll() {
819 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
820 || !mForcedScroll.isAttachedToWindow())) {
821 mForcedScroll = null;
822 }
823 if (mForcedScroll != null) {
824 ExpandableView expandableView = (ExpandableView) mForcedScroll;
825 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
826 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700827 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400828
829 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700830
831 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
832 // that it is not visible anymore.
833 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700834 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400835 }
836 }
837 }
838
Selim Cinek319bdc42014-05-01 23:01:58 +0200839 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200840 if (!mChildrenUpdateRequested) {
841 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
842 mChildrenUpdateRequested = true;
843 invalidate();
844 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200845 }
846
Selim Cinek67b22602014-03-10 15:40:16 +0100847 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200848 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100849 }
850
Selim Cinekf7a14c02014-07-07 14:01:46 +0200851 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100852 int scrollRange = getScrollRange();
853 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700854 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100855 }
856 }
857
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200858 public int getTopPadding() {
859 return mTopPadding;
860 }
861
Selim Cinek1408eb52014-06-02 14:45:38 +0200862 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800863 if (mRegularTopPadding != topPadding) {
864 mRegularTopPadding = topPadding;
865 mDarkTopPadding = topPadding + mDarkSeparatorPadding;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800866 mAmbientState.setDarkTopPadding(mDarkTopPadding);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200867 updateAlgorithmHeightAndPadding();
868 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200869 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200870 mTopPaddingNeedsAnimation = true;
871 mNeedsAnimation = true;
872 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200873 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -0700874 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200875 }
876 }
877
878 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700879 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200880 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700881 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200882 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700883 public void setExpandedHeight(float height) {
884 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700885 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800886 int minExpansionHeight = getMinExpansionHeight();
887 if (height < minExpansionHeight) {
888 mClipRect.left = 0;
889 mClipRect.right = getWidth();
890 mClipRect.top = 0;
891 mClipRect.bottom = (int) height;
892 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700893 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800894 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700895 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800896 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200897 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700898 float translationY;
899 float appearEndPosition = getAppearEndPosition();
900 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800901 float appearFraction = 1.0f;
shawnlin5be1f7c2018-05-21 20:50:54 +0800902 boolean appearing = height < appearEndPosition;
903 mAmbientState.setAppearing(appearing);
904 if (!appearing) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700905 translationY = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +0800906 if (mShouldShowShelfOnly) {
907 stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
908 } else if (mQsExpanded) {
909 int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
910 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
911 if (stackStartPosition <= stackEndPosition) {
912 stackHeight = stackEndPosition;
913 } else {
914 stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
915 stackEndPosition, mQsExpansionFraction);
916 }
917 } else {
918 stackHeight = (int) height;
919 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200920 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800921 appearFraction = getAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -0700922 if (appearFraction >= 0) {
923 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
924 appearFraction);
925 } else {
926 // This may happen when pushing up a heads up. We linearly push it up from the
927 // start
928 translationY = height - appearStartPosition + getExpandTranslationStart();
929 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800930 if (isHeadsUpTransition()) {
931 stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
932 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
933 } else {
934 stackHeight = (int) (height - translationY);
935 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200936 }
937 if (stackHeight != mCurrentStackHeight) {
938 mCurrentStackHeight = stackHeight;
939 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200940 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200941 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700942 setStackTranslation(translationY);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800943 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
944 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
945 listener.accept(mExpandedHeight, appearFraction);
946 }
Selim Cinekcafa87f2016-10-26 17:00:17 -0700947 }
948
949 private void setRequestedClipBounds(Rect clipRect) {
950 mRequestedClipBounds = clipRect;
951 updateClipping();
952 }
953
Lucas Dupin60661a62018-04-12 10:50:13 -0700954 /**
955 * Return the height of the content ignoring the footer.
956 */
957 public int getIntrinsicContentHeight() {
958 return mIntrinsicContentHeight;
959 }
960
Selim Cinekcafa87f2016-10-26 17:00:17 -0700961 public void updateClipping() {
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200962 boolean animatingClipping = mInterpolatedDarkAmount > 0 && mInterpolatedDarkAmount < 1;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700963 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
964 && !mHeadsUpAnimatingAway;
965 if (mIsClipped != clipped) {
966 mIsClipped = clipped;
967 updateFadingState();
968 }
Lucas Dupin16cfe452018-02-08 13:14:50 -0800969
970 if (animatingClipping) {
971 setClipBounds(mBackgroundAnimationRect);
972 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700973 setClipBounds(mRequestedClipBounds);
974 } else {
975 setClipBounds(null);
976 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700977 }
978
979 /**
980 * @return The translation at the beginning when expanding.
981 * Measured relative to the resting position.
982 */
983 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800984 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700985 }
986
987 /**
988 * @return the position from where the appear transition starts when expanding.
989 * Measured in absolute height.
990 */
991 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800992 if (isHeadsUpTransition()) {
993 return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -0700994 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800995 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700996 }
997
998 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900999 * @return the height of the top heads up notification when pinned. This is different from the
1000 * intrinsic height, which also includes whether the notification is system expanded and
1001 * is mainly used when dragging down from a heads up notification.
1002 */
1003 private int getTopHeadsUpPinnedHeight() {
1004 NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
1005 if (topEntry == null) {
1006 return 0;
1007 }
1008 ExpandableNotificationRow row = topEntry.row;
1009 if (row.isChildInGroup()) {
1010 final ExpandableNotificationRow groupSummary
1011 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
1012 if (groupSummary != null) {
1013 row = groupSummary;
1014 }
1015 }
1016 return row.getPinnedHeadsUpHeight();
1017 }
1018
1019 /**
Selim Cinek94c2d822016-07-13 18:50:04 -07001020 * @return the position from where the appear transition ends when expanding.
1021 * Measured in absolute height.
1022 */
1023 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -07001024 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -07001025 int notGoneChildCount = getNotGoneChildCount();
Julia Reynolds34f14962018-05-03 12:40:20 +00001026 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001027 if (isHeadsUpTransition()
Selim Cinekebf42342017-07-13 15:46:10 +02001028 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001029 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +01001030 } else {
1031 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001032 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1033 appearPosition += mShelf.getIntrinsicHeight();
1034 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001035 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001036 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001037 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001038 }
1039 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001040 }
1041
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001042 private boolean isHeadsUpTransition() {
1043 return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
1044 && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
1045 }
1046
Selim Cinek94c2d822016-07-13 18:50:04 -07001047 /**
1048 * @param height the height of the panel
1049 * @return the fraction of the appear animation that has been performed
1050 */
1051 public float getAppearFraction(float height) {
1052 float appearEndPosition = getAppearEndPosition();
1053 float appearStartPosition = getAppearStartPosition();
1054 return (height - appearStartPosition)
1055 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001056 }
1057
Selim Cinekd2281152015-04-10 14:37:46 -07001058 public float getStackTranslation() {
1059 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001060 }
1061
Selim Cinekd2281152015-04-10 14:37:46 -07001062 private void setStackTranslation(float stackTranslation) {
1063 if (stackTranslation != mStackTranslation) {
1064 mStackTranslation = stackTranslation;
1065 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001066 requestChildrenUpdate();
1067 }
Selim Cinek67b22602014-03-10 15:40:16 +01001068 }
1069
1070 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001071 * 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 +01001072 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1073 *
1074 * @return either the layout height or the externally defined height, whichever is smaller
1075 */
Selim Cinek343e6e22014-04-11 21:23:30 +02001076 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001077 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1078 }
1079
Selim Cinek816c8e42015-11-19 12:00:45 -08001080 public int getFirstItemMinHeight() {
1081 final ExpandableView firstChild = getFirstChildNotGone();
1082 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001083 }
1084
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001085 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
Dan Sandler4247a5c2014-07-23 15:58:08 -04001086 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +01001087 }
1088
Jason Monk16ac3772016-02-10 15:39:21 -05001089 public void setQsContainer(ViewGroup qsContainer) {
1090 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001091 }
1092
Rohan Shah524cf7b2018-03-15 14:40:02 -07001093 /**
1094 * Handles cleanup after the given {@code view} has been fully swiped out (including
1095 * re-invoking dismiss logic in case the notification has not made its way out yet).
1096 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001097 @Override
Rohan Shah524cf7b2018-03-15 14:40:02 -07001098 public void onChildDismissed(View view) {
1099 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekd1395642016-04-28 12:22:42 -07001100 if (!row.isDismissed()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001101 handleChildViewDismissed(view);
Selim Cinekd1395642016-04-28 12:22:42 -07001102 }
1103 ViewGroup transientContainer = row.getTransientContainer();
1104 if (transientContainer != null) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001105 transientContainer.removeTransientView(view);
Selim Cinekd1395642016-04-28 12:22:42 -07001106 }
1107 }
1108
Rohan Shah524cf7b2018-03-15 14:40:02 -07001109 /**
1110 * Starts up notification dismiss and tells the notification, if any, to remove itself from
1111 * layout.
1112 *
1113 * @param view view (e.g. notification) to dismiss from the layout
1114 */
1115 private void handleChildViewDismissed(View view) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04001116 if (mDismissAllInProgress) {
1117 return;
1118 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001119
1120 boolean isBlockingHelperShown = false;
1121
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001122 setSwipingInProgress(false);
Rohan Shah524cf7b2018-03-15 14:40:02 -07001123 if (mDragAnimPendingChildren.contains(view)) {
1124 // We start the swipe and finish it in the same frame; we don't want a drag animation.
1125 mDragAnimPendingChildren.remove(view);
Selim Cinekeb973562014-05-02 17:07:49 +02001126 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001127 mAmbientState.onDragFinished(view);
Selim Cinek33223572016-02-19 19:32:22 -08001128 updateContinuousShadowDrawing();
Rohan Shah524cf7b2018-03-15 14:40:02 -07001129
1130 if (view instanceof ExpandableNotificationRow) {
1131 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001132 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -07001133 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001134 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001135 isBlockingHelperShown =
1136 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001137 }
Blazej Magnowski72323322015-07-24 11:49:40 -07001138
Rohan Shah524cf7b2018-03-15 14:40:02 -07001139 if (!isBlockingHelperShown) {
1140 mSwipedOutViews.add(view);
1141 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001142 mFalsingManager.onNotificationDismissed();
1143 if (mFalsingManager.shouldEnforceBouncer()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001144 mStatusBar.executeRunnableDismissingKeyguard(
1145 null,
1146 null /* cancelAction */,
1147 false /* dismissShade */,
1148 true /* afterKeyguardGone */,
1149 false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -07001150 }
Selim Cinekeb973562014-05-02 17:07:49 +02001151 }
1152
1153 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08001154 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001155 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -08001156 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +02001157 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001158 if (mAnimationsEnabled) {
1159 mSnappedBackChildren.add(animView);
1160 mNeedsAnimation = true;
1161 }
Selim Cinekeb973562014-05-02 17:07:49 +02001162 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +02001163 } else {
1164 // We start the swipe and snap back in the same frame, we don't want any animation
1165 mDragAnimPendingChildren.remove(animView);
1166 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001167 if (mCurrMenuRow != null && targetLeft == 0) {
1168 mCurrMenuRow.resetMenu();
1169 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08001170 }
Selim Cinek67b22602014-03-10 15:40:16 +01001171 }
1172
Adrian Roos5d9cc662014-05-28 17:08:13 +02001173 @Override
1174 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Anthony Chen7acbb772017-04-07 16:45:25 -07001175 // Returning true prevents alpha fading.
1176 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +02001177 }
1178
Mady Mellorb0a82462016-04-30 17:31:02 -07001179 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001180 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001181 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001182 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001183 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -08001184 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -07001185 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001186 mDragAnimPendingChildren.add(v);
1187 mNeedsAnimation = true;
1188 }
Selim Cinekeb973562014-05-02 17:07:49 +02001189 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +01001190 }
1191
Selim Cinek684a4422015-04-15 16:18:39 -07001192 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001193 if (v instanceof ExpandableNotificationRow) {
1194 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001195 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001196 }
1197 return false;
1198 }
1199
1200 private boolean isHeadsUp(View v) {
1201 if (v instanceof ExpandableNotificationRow) {
1202 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1203 return row.isHeadsUp();
1204 }
1205 return false;
1206 }
1207
Mady Mellorb0a82462016-04-30 17:31:02 -07001208 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001209 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001210 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001211 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001212 }
1213
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001214 @Override
1215 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001216 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001217 }
1218
Mady Mellor4b80b102016-01-22 08:03:58 -08001219 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001220 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001221 View child = getChildAtPosition(ev.getX(), ev.getY());
1222 if (child instanceof ExpandableNotificationRow) {
1223 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1224 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001225 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001226 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001227 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001228 || (parent.getNotificationChildren().size() == 1
1229 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001230 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001231 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001232 // child notifications so we use the parent of the child. We also do the same
1233 // if we only have a single child.
1234 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001235 }
1236 }
1237 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001238 }
1239
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001240 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1241 getLocationOnScreen(mTempInt2);
1242 float localTouchY = touchY - mTempInt2[1];
1243
1244 ExpandableView closestChild = null;
1245 float minDist = Float.MAX_VALUE;
1246
1247 // find the view closest to the location, accounting for GONE views
1248 final int count = getChildCount();
1249 for (int childIdx = 0; childIdx < count; childIdx++) {
1250 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1251 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001252 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001253 continue;
1254 }
1255 float childTop = slidingChild.getTranslationY();
1256 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001257 float bottom = childTop + slidingChild.getActualHeight()
1258 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001259
1260 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1261 if (dist < minDist) {
1262 closestChild = slidingChild;
1263 minDist = dist;
1264 }
1265 }
1266 return closestChild;
1267 }
1268
Mady Mellorb0a82462016-04-30 17:31:02 -07001269 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001270 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001271 getLocationOnScreen(mTempInt2);
1272 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001273 }
1274
Mady Mellorb0a82462016-04-30 17:31:02 -07001275 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001276 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001277 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1278
1279 }
1280
1281 /**
1282 * Get the child at a certain screen location.
1283 *
1284 * @param touchX the x coordinate
1285 * @param touchY the y coordinate
1286 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1287 * @return the child at the given location.
1288 */
1289 private ExpandableView getChildAtPosition(float touchX, float touchY,
1290 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001291 // find the view under the pointer, accounting for GONE views
1292 final int count = getChildCount();
1293 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001294 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001295 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001296 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001297 continue;
1298 }
Selim Cinek89faff12014-06-19 16:29:04 -07001299 float childTop = slidingChild.getTranslationY();
1300 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001301 float bottom = childTop + slidingChild.getActualHeight()
1302 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001303
1304 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1305 // camera affordance).
1306 int left = 0;
1307 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001308
Selim Cinek34ed7c02017-09-08 15:03:12 -07001309 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001310 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001311 if (slidingChild instanceof ExpandableNotificationRow) {
1312 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001313 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001314 && mHeadsUpManager.getTopEntry().row != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001315 && mGroupManager.getGroupSummary(
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001316 mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
Selim Cinek5bc852a2015-12-21 12:19:09 -08001317 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001318 continue;
1319 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001320 return row.getViewAtPosition(touchY - childTop);
1321 }
Selim Cinek67b22602014-03-10 15:40:16 +01001322 return slidingChild;
1323 }
1324 }
1325 return null;
1326 }
1327
Mady Mellorb0a82462016-04-30 17:31:02 -07001328 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001329 public boolean canChildBeExpanded(View v) {
1330 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001331 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001332 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001333 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001334 }
1335
Chris Wren6abeeb92016-05-26 14:44:38 -04001336 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001337 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001338 public void setUserExpandedChild(View v, boolean userExpanded) {
1339 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001340 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001341 if (userExpanded && onKeyguard()) {
1342 // Due to a race when locking the screen while touching, a notification may be
1343 // expanded even after we went back to keyguard. An example of this happens if
1344 // you click in the empty space while expanding a group.
1345
1346 // We also need to un-user lock it here, since otherwise the content height
1347 // calculated might be wrong. We also can't invert the two calls since
1348 // un-userlocking it will trigger a layout switch in the content view.
1349 row.setUserLocked(false);
1350 updateContentHeight();
1351 notifyHeightChangeListener(row);
1352 return;
1353 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001354 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1355 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001356 }
1357 }
1358
Mady Mellorb0a82462016-04-30 17:31:02 -07001359 @Override
1360 public void setExpansionCancelled(View v) {
1361 if (v instanceof ExpandableNotificationRow) {
1362 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1363 }
1364 }
1365
1366 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001367 public void setUserLockedChild(View v, boolean userLocked) {
1368 if (v instanceof ExpandableNotificationRow) {
1369 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1370 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001371 cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02001372 requestDisallowInterceptTouchEvent(true);
1373 }
1374
1375 @Override
1376 public void expansionStateChanged(boolean isExpanding) {
1377 mExpandingNotification = isExpanding;
1378 if (!mExpandedInThisMotion) {
1379 mMaxScrollAfterExpand = mOwnScrollY;
1380 mExpandedInThisMotion = true;
1381 }
1382 }
1383
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001384 @Override
1385 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001386 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001387 }
1388
Selim Cinek1408eb52014-06-02 14:45:38 +02001389 public void setScrollingEnabled(boolean enable) {
1390 mScrollingEnabled = enable;
1391 }
1392
Adrian Roos181385c2016-05-05 17:45:44 -04001393 public void lockScrollTo(View v) {
1394 if (mForcedScroll == v) {
1395 return;
1396 }
1397 mForcedScroll = v;
1398 scrollTo(v);
1399 }
1400
Ricky Waicd35def2016-05-03 11:07:07 +01001401 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001402 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001403 int positionInLinearLayout = getPositionInLinearLayout(v);
1404 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1405 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001406
Adrian Roos4a579672016-05-24 16:54:37 -07001407 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1408 // that it is not visible anymore.
1409 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001410 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1411 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001412 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001413 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001414 }
Ricky Waicd35def2016-05-03 11:07:07 +01001415 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001416 }
1417
Adrian Roos181385c2016-05-05 17:45:44 -04001418 /**
1419 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1420 * the IME.
1421 */
1422 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1423 return positionInLinearLayout + v.getIntrinsicHeight() +
felkachang6d1eca32018-07-04 12:51:44 +08001424 getImeInset() - getHeight()
1425 + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
Adrian Roos181385c2016-05-05 17:45:44 -04001426 }
1427
Adrian Roos5153d4a2016-03-22 10:01:56 -07001428 @Override
1429 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001430 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001431
1432 int range = getScrollRange();
1433 if (mOwnScrollY > range) {
1434 // HACK: We're repeatedly getting staggered insets here while the IME is
1435 // animating away. To work around that we'll wait until things have settled.
1436 removeCallbacks(mReclamp);
1437 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001438 } else if (mForcedScroll != null) {
1439 // The scroll was requested before we got the actual inset - in case we need
1440 // to scroll up some more do so now.
1441 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001442 }
1443 return insets;
1444 }
1445
1446 private Runnable mReclamp = new Runnable() {
1447 @Override
1448 public void run() {
1449 int range = getScrollRange();
1450 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1451 mDontReportNextOverScroll = true;
1452 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001453 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001454 }
1455 };
1456
Selim Cinek1408eb52014-06-02 14:45:38 +02001457 public void setExpandingEnabled(boolean enable) {
1458 mExpandHelper.setEnabled(enable);
1459 }
1460
1461 private boolean isScrollingEnabled() {
1462 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001463 }
1464
Mady Mellorb0a82462016-04-30 17:31:02 -07001465 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001466 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001467 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001468 }
1469
Selim Cinek19c8c702014-08-25 22:09:19 +02001470 @Override
1471 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001472 return onKeyguard();
1473 }
1474
1475 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001476 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001477 }
1478
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001479 private void setSwipingInProgress(boolean isSwiped) {
1480 mSwipingInProgress = isSwiped;
1481 if(isSwiped) {
1482 requestDisallowInterceptTouchEvent(true);
1483 }
Selim Cinek67b22602014-03-10 15:40:16 +01001484 }
1485
1486 @Override
1487 protected void onConfigurationChanged(Configuration newConfig) {
1488 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001489 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001490 float densityScale = getResources().getDisplayMetrics().density;
1491 mSwipeHelper.setDensityScale(densityScale);
1492 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1493 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1494 initView(getContext());
1495 }
1496
Dan Sandlereceda3d2014-07-21 15:35:01 -04001497 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001498 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1499 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001500 }
1501
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001502 @Override
Mady Mellor86889c22016-04-18 16:37:06 -07001503 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001504 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001505 // If the child is showing the notification menu snap to that
1506 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001507 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001508 }
1509
Selim Cinek67b22602014-03-10 15:40:16 +01001510 @Override
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001511 public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
1512 return this;
1513 }
1514
1515 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001516 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001517 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1518 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001519 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001520 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001521 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001522 if (isCancelOrUp) {
1523 mExpandHelper.onlyObserveMovements(false);
1524 }
1525 boolean wasExpandingBefore = mExpandingNotification;
1526 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001527 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1528 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001529 dispatchDownEventToScroller(ev);
1530 }
1531 }
Selim Cinek67b22602014-03-10 15:40:16 +01001532 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001533 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1534 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001535 scrollerWantsIt = onScrollTouch(ev);
1536 }
1537 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001538 if (!mIsBeingDragged
1539 && !mExpandingNotification
1540 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001541 && !mOnlyScrollingInThisMotion
1542 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001543 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1544 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001545
1546 // Check if we need to clear any snooze leavebehinds
Eliot Courtney47098cb2017-10-18 17:30:30 +09001547 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07001548 if (guts != null && !isTouchInView(ev, guts)
1549 && guts.getGutsContent() instanceof NotificationSnooze) {
1550 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1551 if ((ns.isExpanded() && isCancelOrUp)
1552 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1553 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1554 // clear it on the next non-horizontal swipe or expand event.
1555 checkSnoozeLeavebehind();
1556 }
1557 }
1558 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1559 mCheckForLeavebehind = true;
1560 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001561 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1562 }
1563
1564 private void dispatchDownEventToScroller(MotionEvent ev) {
1565 MotionEvent downEvent = MotionEvent.obtain(ev);
1566 downEvent.setAction(MotionEvent.ACTION_DOWN);
1567 onScrollTouch(downEvent);
1568 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001569 }
1570
Selim Cinek11e33232016-08-05 15:30:53 -07001571 @Override
1572 public boolean onGenericMotionEvent(MotionEvent event) {
1573 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1574 || mDisallowScrollingInThisMotion) {
1575 return false;
1576 }
1577 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1578 switch (event.getAction()) {
1579 case MotionEvent.ACTION_SCROLL: {
1580 if (!mIsBeingDragged) {
1581 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1582 if (vscroll != 0) {
1583 final int delta = (int) (vscroll * getVerticalScrollFactor());
1584 final int range = getScrollRange();
1585 int oldScrollY = mOwnScrollY;
1586 int newScrollY = oldScrollY - delta;
1587 if (newScrollY < 0) {
1588 newScrollY = 0;
1589 } else if (newScrollY > range) {
1590 newScrollY = range;
1591 }
1592 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001593 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001594 return true;
1595 }
1596 }
1597 }
1598 }
1599 }
1600 }
1601 return super.onGenericMotionEvent(event);
1602 }
1603
Selim Cinek67b22602014-03-10 15:40:16 +01001604 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001605 if (!isScrollingEnabled()) {
1606 return false;
1607 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001608 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001609 return false;
1610 }
Adrian Roos181385c2016-05-05 17:45:44 -04001611 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001612 initVelocityTrackerIfNotExists();
1613 mVelocityTracker.addMovement(ev);
1614
1615 final int action = ev.getAction();
1616
1617 switch (action & MotionEvent.ACTION_MASK) {
1618 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001619 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001620 return false;
1621 }
1622 boolean isBeingDragged = !mScroller.isFinished();
1623 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001624 /*
1625 * If being flinged and user touches, stop the fling. isFinished
1626 * will be false if being flinged.
1627 */
1628 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001629 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001630 }
1631
1632 // Remember where the motion event started
1633 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001634 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001635 mActivePointerId = ev.getPointerId(0);
1636 break;
1637 }
1638 case MotionEvent.ACTION_MOVE:
1639 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1640 if (activePointerIndex == -1) {
1641 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1642 break;
1643 }
1644
1645 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001646 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001647 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001648 final int xDiff = Math.abs(x - mDownX);
1649 final int yDiff = Math.abs(deltaY);
1650 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001651 setIsBeingDragged(true);
1652 if (deltaY > 0) {
1653 deltaY -= mTouchSlop;
1654 } else {
1655 deltaY += mTouchSlop;
1656 }
1657 }
1658 if (mIsBeingDragged) {
1659 // Scroll to follow the motion event
1660 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001661 int range = getScrollRange();
1662 if (mExpandedInThisMotion) {
1663 range = Math.min(range, mMaxScrollAfterExpand);
1664 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001665
1666 float scrollAmount;
1667 if (deltaY < 0) {
1668 scrollAmount = overScrollDown(deltaY);
1669 } else {
1670 scrollAmount = overScrollUp(deltaY, range);
1671 }
Selim Cinek67b22602014-03-10 15:40:16 +01001672
Selim Cinek9212de82017-02-06 16:04:28 -08001673 // Calling customOverScrollBy will call onCustomOverScrolled, which
1674 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001675 if (scrollAmount != 0.0f) {
1676 // The scrolling motion could not be compensated with the
1677 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001678 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1679 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001680 // If we're scrolling, leavebehinds should be dismissed
1681 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001682 }
Selim Cinek67b22602014-03-10 15:40:16 +01001683 }
1684 break;
1685 case MotionEvent.ACTION_UP:
1686 if (mIsBeingDragged) {
1687 final VelocityTracker velocityTracker = mVelocityTracker;
1688 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1689 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1690
Selim Cinek1408eb52014-06-02 14:45:38 +02001691 if (shouldOverScrollFling(initialVelocity)) {
1692 onOverScrollFling(true, initialVelocity);
1693 } else {
1694 if (getChildCount() > 0) {
1695 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1696 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1697 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1698 fling(-initialVelocity);
1699 } else {
1700 onOverScrollFling(false, initialVelocity);
1701 }
1702 } else {
1703 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1704 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001705 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001706 }
Selim Cinek67b22602014-03-10 15:40:16 +01001707 }
1708 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001709 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001710 mActivePointerId = INVALID_POINTER;
1711 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001712 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001713
Selim Cinek67b22602014-03-10 15:40:16 +01001714 break;
1715 case MotionEvent.ACTION_CANCEL:
1716 if (mIsBeingDragged && getChildCount() > 0) {
1717 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001718 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001719 }
1720 mActivePointerId = INVALID_POINTER;
1721 endDrag();
1722 }
1723 break;
1724 case MotionEvent.ACTION_POINTER_DOWN: {
1725 final int index = ev.getActionIndex();
1726 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001727 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001728 mActivePointerId = ev.getPointerId(index);
1729 break;
1730 }
1731 case MotionEvent.ACTION_POINTER_UP:
1732 onSecondaryPointerUp(ev);
1733 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001734 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001735 break;
1736 }
1737 return true;
1738 }
1739
Muyuan Li84b45612016-06-01 11:05:08 -07001740 protected boolean isInsideQsContainer(MotionEvent ev) {
1741 return ev.getY() < mQsContainer.getBottom();
1742 }
1743
Selim Cinek1408eb52014-06-02 14:45:38 +02001744 private void onOverScrollFling(boolean open, int initialVelocity) {
1745 if (mOverscrollTopChangedListener != null) {
1746 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1747 }
1748 mDontReportNextOverScroll = true;
1749 setOverScrollAmount(0.0f, true, false);
1750 }
1751
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001752 /**
1753 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1754 *
1755 * @param deltaY The amount to scroll upwards, has to be positive.
1756 * @return The amount of scrolling to be performed by the scroller,
1757 * not handled by the overScroll amount.
1758 */
1759 private float overScrollUp(int deltaY, int range) {
1760 deltaY = Math.max(deltaY, 0);
1761 float currentTopAmount = getCurrentOverScrollAmount(true);
1762 float newTopAmount = currentTopAmount - deltaY;
1763 if (currentTopAmount > 0) {
1764 setOverScrollAmount(newTopAmount, true /* onTop */,
1765 false /* animate */);
1766 }
1767 // Top overScroll might not grab all scrolling motion,
1768 // we have to scroll as well.
1769 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1770 float newScrollY = mOwnScrollY + scrollAmount;
1771 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001772 if (!mExpandedInThisMotion) {
1773 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1774 // We overScroll on the top
1775 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1776 false /* onTop */,
1777 false /* animate */);
1778 }
Selim Cinekef406062016-09-29 17:33:13 -07001779 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001780 scrollAmount = 0.0f;
1781 }
1782 return scrollAmount;
1783 }
1784
1785 /**
1786 * Perform a scroll downward and adapt the overscroll amounts accordingly
1787 *
1788 * @param deltaY The amount to scroll downwards, has to be negative.
1789 * @return The amount of scrolling to be performed by the scroller,
1790 * not handled by the overScroll amount.
1791 */
1792 private float overScrollDown(int deltaY) {
1793 deltaY = Math.min(deltaY, 0);
1794 float currentBottomAmount = getCurrentOverScrollAmount(false);
1795 float newBottomAmount = currentBottomAmount + deltaY;
1796 if (currentBottomAmount > 0) {
1797 setOverScrollAmount(newBottomAmount, false /* onTop */,
1798 false /* animate */);
1799 }
1800 // Bottom overScroll might not grab all scrolling motion,
1801 // we have to scroll as well.
1802 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1803 float newScrollY = mOwnScrollY + scrollAmount;
1804 if (newScrollY < 0) {
1805 float currentTopPixels = getCurrentOverScrolledPixels(true);
1806 // We overScroll on the top
1807 setOverScrolledPixels(currentTopPixels - newScrollY,
1808 true /* onTop */,
1809 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001810 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001811 scrollAmount = 0.0f;
1812 }
1813 return scrollAmount;
1814 }
1815
Selim Cinek67b22602014-03-10 15:40:16 +01001816 private void onSecondaryPointerUp(MotionEvent ev) {
1817 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1818 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1819 final int pointerId = ev.getPointerId(pointerIndex);
1820 if (pointerId == mActivePointerId) {
1821 // This was our active pointer going up. Choose a new
1822 // active pointer and adjust accordingly.
1823 // TODO: Make this decision more intelligent.
1824 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1825 mLastMotionY = (int) ev.getY(newPointerIndex);
1826 mActivePointerId = ev.getPointerId(newPointerIndex);
1827 if (mVelocityTracker != null) {
1828 mVelocityTracker.clear();
1829 }
1830 }
1831 }
1832
1833 private void initVelocityTrackerIfNotExists() {
1834 if (mVelocityTracker == null) {
1835 mVelocityTracker = VelocityTracker.obtain();
1836 }
1837 }
1838
1839 private void recycleVelocityTracker() {
1840 if (mVelocityTracker != null) {
1841 mVelocityTracker.recycle();
1842 mVelocityTracker = null;
1843 }
1844 }
1845
1846 private void initOrResetVelocityTracker() {
1847 if (mVelocityTracker == null) {
1848 mVelocityTracker = VelocityTracker.obtain();
1849 } else {
1850 mVelocityTracker.clear();
1851 }
1852 }
1853
Ricky Waicd35def2016-05-03 11:07:07 +01001854 public void setFinishScrollingCallback(Runnable runnable) {
1855 mFinishScrollingCallback = runnable;
1856 }
1857
Selim Cinek9212de82017-02-06 16:04:28 -08001858 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001859 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001860 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001861 int y = mScroller.getCurrY();
1862
Selim Cinek9212de82017-02-06 16:04:28 -08001863 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001864 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001865 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1866 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001867 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001868 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1869 }
1870 }
Selim Cinek67b22602014-03-10 15:40:16 +01001871
Adrian Roos5153d4a2016-03-22 10:01:56 -07001872 if (mDontClampNextScroll) {
1873 range = Math.max(range, oldY);
1874 }
Selim Cinek9212de82017-02-06 16:04:28 -08001875 customOverScrollBy(y - oldY, oldY, range,
1876 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001877 }
1878
Selim Cinek9212de82017-02-06 16:04:28 -08001879 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001880 } else {
1881 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001882 if (mFinishScrollingCallback != null) {
1883 mFinishScrollingCallback.run();
1884 }
Selim Cinek67b22602014-03-10 15:40:16 +01001885 }
1886 }
1887
Selim Cinek9212de82017-02-06 16:04:28 -08001888 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1889 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001890
1891 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001892 final int top = -maxOverScrollY;
1893 final int bottom = maxOverScrollY + scrollRangeY;
1894
1895 boolean clampedY = false;
1896 if (newScrollY > bottom) {
1897 newScrollY = bottom;
1898 clampedY = true;
1899 } else if (newScrollY < top) {
1900 newScrollY = top;
1901 clampedY = true;
1902 }
1903
Selim Cinek9212de82017-02-06 16:04:28 -08001904 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001905
1906 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001907 }
1908
1909 /**
1910 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1911 * overscroll effect based on numPixels. By default this will also cancel animations on the
1912 * same overScroll edge.
1913 *
1914 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1915 * the rubber-banding logic.
1916 * @param onTop Should the effect be applied on top of the scroller.
1917 * @param animate Should an animation be performed.
1918 */
1919 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001920 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001921 }
1922
1923 /**
1924 * Set the effective overScroll amount which will be directly reflected in the layout.
1925 * By default this will also cancel animations on the same overScroll edge.
1926 *
1927 * @param amount The amount to overScroll by.
1928 * @param onTop Should the effect be applied on top of the scroller.
1929 * @param animate Should an animation be performed.
1930 */
1931 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1932 setOverScrollAmount(amount, onTop, animate, true);
1933 }
1934
1935 /**
1936 * Set the effective overScroll amount which will be directly reflected in the layout.
1937 *
1938 * @param amount The amount to overScroll by.
1939 * @param onTop Should the effect be applied on top of the scroller.
1940 * @param animate Should an animation be performed.
1941 * @param cancelAnimators Should running animations be cancelled.
1942 */
1943 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1944 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001945 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1946 }
1947
1948 /**
1949 * Set the effective overScroll amount which will be directly reflected in the layout.
1950 *
1951 * @param amount The amount to overScroll by.
1952 * @param onTop Should the effect be applied on top of the scroller.
1953 * @param animate Should an animation be performed.
1954 * @param cancelAnimators Should running animations be cancelled.
1955 * @param isRubberbanded The value which will be passed to
1956 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1957 */
1958 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1959 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001960 if (cancelAnimators) {
1961 mStateAnimator.cancelOverScrollAnimators(onTop);
1962 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001963 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001964 }
1965
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001966 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1967 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001968 amount = Math.max(0, amount);
1969 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001970 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001971 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001972 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001973 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001974 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001975 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001976 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001977 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001978 }
1979 }
1980
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001981 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001982 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1983 if (mDontReportNextOverScroll) {
1984 mDontReportNextOverScroll = false;
1985 return;
1986 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001987 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001988 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001989 }
1990 }
1991
1992 public void setOverscrollTopChangedListener(
1993 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1994 mOverscrollTopChangedListener = overscrollTopChangedListener;
1995 }
1996
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001997 public float getCurrentOverScrollAmount(boolean top) {
1998 return mAmbientState.getOverScrollAmount(top);
1999 }
2000
2001 public float getCurrentOverScrolledPixels(boolean top) {
2002 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
2003 }
2004
2005 private void setOverScrolledPixels(float amount, boolean onTop) {
2006 if (onTop) {
2007 mOverScrolledTopPixels = amount;
2008 } else {
2009 mOverScrolledBottomPixels = amount;
2010 }
2011 }
2012
Selim Cinek9212de82017-02-06 16:04:28 -08002013 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002014 // Treat animating scrolls differently; see #computeScroll() for why.
2015 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07002016 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002017 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002018 springBack();
2019 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02002020 float overScrollTop = getCurrentOverScrollAmount(true);
2021 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002022 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002023 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002024 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002025 }
Selim Cinek67b22602014-03-10 15:40:16 +01002026 }
Selim Cinek67b22602014-03-10 15:40:16 +01002027 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08002028 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002029 }
2030 }
2031
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002032 private void springBack() {
2033 int scrollRange = getScrollRange();
2034 boolean overScrolledTop = mOwnScrollY <= 0;
2035 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2036 if (overScrolledTop || overScrolledBottom) {
2037 boolean onTop;
2038 float newAmount;
2039 if (overScrolledTop) {
2040 onTop = true;
2041 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07002042 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02002043 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002044 } else {
2045 onTop = false;
2046 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07002047 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002048 }
2049 setOverScrollAmount(newAmount, onTop, false);
2050 setOverScrollAmount(0.0f, onTop, true);
2051 mScroller.forceFinished(true);
2052 }
2053 }
2054
Selim Cinek67b22602014-03-10 15:40:16 +01002055 private int getScrollRange() {
felkachang6d1eca32018-07-04 12:51:44 +08002056 // In current design, it only use the top HUN to treat all of HUNs
2057 // although there are more than one HUNs
2058 int contentHeight = mContentHeight;
2059 if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
2060 contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
2061 }
2062 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002063 int imeInset = getImeInset();
felkachang6d1eca32018-07-04 12:51:44 +08002064 scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
Selim Cineka424c502016-04-05 13:07:54 -07002065 return scrollRange;
2066 }
2067
2068 private int getImeInset() {
2069 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002070 }
2071
Selim Cinek343e6e22014-04-11 21:23:30 +02002072 /**
2073 * @return the first child which has visibility unequal to GONE
2074 */
Selim Cinekb55386d2015-12-16 17:26:49 -08002075 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002076 int childCount = getChildCount();
2077 for (int i = 0; i < childCount; i++) {
2078 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002079 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002080 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002081 }
2082 }
2083 return null;
2084 }
2085
Selim Cinek4a1ac842014-05-01 15:51:58 +02002086 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002087 * @return the child before the given view which has visibility unequal to GONE
2088 */
2089 public ExpandableView getViewBeforeView(ExpandableView view) {
2090 ExpandableView previousView = null;
2091 int childCount = getChildCount();
2092 for (int i = 0; i < childCount; i++) {
2093 View child = getChildAt(i);
2094 if (child == view) {
2095 return previousView;
2096 }
2097 if (child.getVisibility() != View.GONE) {
2098 previousView = (ExpandableView) child;
2099 }
2100 }
2101 return null;
2102 }
2103
2104 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002105 * @return The first child which has visibility unequal to GONE which is currently below the
2106 * given translationY or equal to it.
2107 */
Selim Cinekef8c2252017-02-10 14:52:18 -08002108 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002109 int childCount = getChildCount();
2110 for (int i = 0; i < childCount; i++) {
2111 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002112 if (child.getVisibility() == View.GONE) {
2113 continue;
2114 }
2115 float rowTranslation = child.getTranslationY();
2116 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002117 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002118 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2119 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2120 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2121 List<ExpandableNotificationRow> notificationChildren =
2122 row.getNotificationChildren();
2123 for (int childIndex = 0; childIndex < notificationChildren.size();
2124 childIndex++) {
2125 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2126 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2127 return rowChild;
2128 }
2129 }
2130 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002131 }
2132 }
2133 return null;
2134 }
2135
2136 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002137 * @return the last child which has visibility unequal to GONE
2138 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002139 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002140 int childCount = getChildCount();
2141 for (int i = childCount - 1; i >= 0; i--) {
2142 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002143 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002144 return child;
2145 }
2146 }
2147 return null;
2148 }
2149
Jorim Jaggi069cd032014-05-15 03:09:01 +02002150 /**
2151 * @return the number of children which have visibility unequal to GONE
2152 */
2153 public int getNotGoneChildCount() {
2154 int childCount = getChildCount();
2155 int count = 0;
2156 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002157 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002158 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002159 count++;
2160 }
2161 }
2162 return count;
2163 }
2164
Selim Cinek67b22602014-03-10 15:40:16 +01002165 private void updateContentHeight() {
2166 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002167 float previousPaddingRequest = mPaddingBetweenElements;
2168 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002169 int numShownItems = 0;
2170 boolean finish = false;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002171 int maxDisplayedNotifications = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002172 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08002173 : mMaxDisplayedNotifications;
2174
Selim Cinek67b22602014-03-10 15:40:16 +01002175 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002176 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002177 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002178 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002179 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002180 boolean limitReached = maxDisplayedNotifications != -1
2181 && numShownItems >= maxDisplayedNotifications;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002182 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002183 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07002184 && expandableView instanceof ExpandableNotificationRow
2185 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
2186 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002187 expandableView = mShelf;
2188 finish = true;
2189 }
Selim Cinek42357e02016-02-24 18:48:01 -08002190 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002191 float padding;
2192 if (increasedPaddingAmount >= 0.0f) {
2193 padding = (int) NotificationUtils.interpolate(
2194 previousPaddingRequest,
2195 mIncreasedPaddingBetweenElements,
2196 increasedPaddingAmount);
2197 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002198 mPaddingBetweenElements,
2199 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002200 increasedPaddingAmount);
2201 } else {
2202 int ownPadding = (int) NotificationUtils.interpolate(
2203 0,
2204 mPaddingBetweenElements,
2205 1.0f + increasedPaddingAmount);
2206 if (previousPaddingAmount > 0.0f) {
2207 padding = (int) NotificationUtils.interpolate(
2208 ownPadding,
2209 mIncreasedPaddingBetweenElements,
2210 previousPaddingAmount);
2211 } else {
2212 padding = ownPadding;
2213 }
2214 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002215 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002216 if (height != 0) {
2217 height += padding;
2218 }
2219 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002220 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002221 numShownItems++;
2222 if (finish) {
2223 break;
2224 }
Selim Cinek67b22602014-03-10 15:40:16 +01002225 }
2226 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002227 mIntrinsicContentHeight = height;
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002228
2229 // We don't want to use the toppadding since that might be interpolated and we want
2230 // to take the final value of the animation.
2231 int topPadding = mAmbientState.isFullyDark() ? mDarkTopPadding : mRegularTopPadding;
2232 mContentHeight = height + topPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002233 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002234 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002235 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002236 }
2237
Adrian Roos7d062c42017-03-30 15:11:43 -07002238 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002239 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002240 }
2241
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002242 @Override
Selim Cinekbe2c4432017-05-30 12:11:09 -07002243 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002244 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002245 }
2246
Selim Cinekc22fff62016-05-20 12:44:30 -07002247 private void updateScrollability() {
Riddle Hsu065c01c2018-05-10 23:14:19 +08002248 boolean scrollable = !mQsExpanded && getScrollRange() > 0;
Selim Cinekc22fff62016-05-20 12:44:30 -07002249 if (scrollable != mScrollable) {
2250 mScrollable = scrollable;
2251 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002252 updateForwardAndBackwardScrollability();
2253 }
2254 }
2255
2256 private void updateForwardAndBackwardScrollability() {
2257 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2258 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2259 boolean changed = forwardScrollable != mForwardScrollable
2260 || backwardsScrollable != mBackwardScrollable;
2261 mForwardScrollable = forwardScrollable;
2262 mBackwardScrollable = backwardsScrollable;
2263 if (changed) {
2264 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002265 }
Selim Cinek67b22602014-03-10 15:40:16 +01002266 }
2267
Selim Cinek6811d722016-01-19 17:53:12 -08002268 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002269 // No need to update the background color if it's not being drawn.
Lucas Dupin16cfe452018-02-08 13:14:50 -08002270 if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002271 return;
2272 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002273
Selim Cinek6811d722016-01-19 17:53:12 -08002274 updateBackgroundBounds();
2275 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002276 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2277 || areBoundsAnimating();
2278 if (!isExpanded()) {
2279 abortBackgroundAnimators();
2280 animate = false;
2281 }
2282 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002283 startBackgroundAnimation();
2284 } else {
2285 mCurrentBounds.set(mBackgroundBounds);
2286 applyCurrentBackgroundBounds();
2287 }
2288 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002289 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002290 }
Selim Cinek614576e2016-01-20 10:54:09 -08002291 mAnimateNextBackgroundBottom = false;
2292 mAnimateNextBackgroundTop = false;
2293 }
2294
Selim Cinek54680902016-10-19 16:49:44 -07002295 private void abortBackgroundAnimators() {
2296 if (mBottomAnimator != null) {
2297 mBottomAnimator.cancel();
2298 }
2299 if (mTopAnimator != null) {
2300 mTopAnimator.cancel();
2301 }
2302 }
2303
Selim Cinek614576e2016-01-20 10:54:09 -08002304 private boolean areBoundsAnimating() {
2305 return mBottomAnimator != null || mTopAnimator != null;
2306 }
2307
2308 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002309 // left and right are always instantly applied
2310 mCurrentBounds.left = mBackgroundBounds.left;
2311 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002312 startBottomAnimation();
2313 startTopAnimation();
2314 }
2315
2316 private void startTopAnimation() {
2317 int previousEndValue = mEndAnimationRect.top;
2318 int newEndValue = mBackgroundBounds.top;
2319 ObjectAnimator previousAnimator = mTopAnimator;
2320 if (previousAnimator != null && previousEndValue == newEndValue) {
2321 return;
2322 }
2323 if (!mAnimateNextBackgroundTop) {
2324 // just a local update was performed
2325 if (previousAnimator != null) {
2326 // we need to increase all animation keyframes of the previous animator by the
2327 // relative change to the end value
2328 int previousStartValue = mStartAnimationRect.top;
2329 PropertyValuesHolder[] values = previousAnimator.getValues();
2330 values[0].setIntValues(previousStartValue, newEndValue);
2331 mStartAnimationRect.top = previousStartValue;
2332 mEndAnimationRect.top = newEndValue;
2333 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2334 return;
2335 } else {
2336 // no new animation needed, let's just apply the value
2337 setBackgroundTop(newEndValue);
2338 return;
2339 }
2340 }
2341 if (previousAnimator != null) {
2342 previousAnimator.cancel();
2343 }
2344 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2345 mCurrentBounds.top, newEndValue);
2346 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2347 animator.setInterpolator(interpolator);
2348 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2349 // remove the tag when the animation is finished
2350 animator.addListener(new AnimatorListenerAdapter() {
2351 @Override
2352 public void onAnimationEnd(Animator animation) {
2353 mStartAnimationRect.top = -1;
2354 mEndAnimationRect.top = -1;
2355 mTopAnimator = null;
2356 }
2357 });
2358 animator.start();
2359 mStartAnimationRect.top = mCurrentBounds.top;
2360 mEndAnimationRect.top = newEndValue;
2361 mTopAnimator = animator;
2362 }
2363
2364 private void startBottomAnimation() {
2365 int previousStartValue = mStartAnimationRect.bottom;
2366 int previousEndValue = mEndAnimationRect.bottom;
2367 int newEndValue = mBackgroundBounds.bottom;
2368 ObjectAnimator previousAnimator = mBottomAnimator;
2369 if (previousAnimator != null && previousEndValue == newEndValue) {
2370 return;
2371 }
2372 if (!mAnimateNextBackgroundBottom) {
2373 // just a local update was performed
2374 if (previousAnimator != null) {
2375 // we need to increase all animation keyframes of the previous animator by the
2376 // relative change to the end value
2377 PropertyValuesHolder[] values = previousAnimator.getValues();
2378 values[0].setIntValues(previousStartValue, newEndValue);
2379 mStartAnimationRect.bottom = previousStartValue;
2380 mEndAnimationRect.bottom = newEndValue;
2381 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2382 return;
2383 } else {
2384 // no new animation needed, let's just apply the value
2385 setBackgroundBottom(newEndValue);
2386 return;
2387 }
2388 }
2389 if (previousAnimator != null) {
2390 previousAnimator.cancel();
2391 }
2392 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2393 mCurrentBounds.bottom, newEndValue);
2394 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2395 animator.setInterpolator(interpolator);
2396 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2397 // remove the tag when the animation is finished
2398 animator.addListener(new AnimatorListenerAdapter() {
2399 @Override
2400 public void onAnimationEnd(Animator animation) {
2401 mStartAnimationRect.bottom = -1;
2402 mEndAnimationRect.bottom = -1;
2403 mBottomAnimator = null;
2404 }
2405 });
2406 animator.start();
2407 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2408 mEndAnimationRect.bottom = newEndValue;
2409 mBottomAnimator = animator;
2410 }
2411
2412 private void setBackgroundTop(int top) {
2413 mCurrentBounds.top = top;
2414 applyCurrentBackgroundBounds();
2415 }
2416
2417 public void setBackgroundBottom(int bottom) {
2418 mCurrentBounds.bottom = bottom;
2419 applyCurrentBackgroundBounds();
2420 }
2421
2422 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002423 // If the background of the notification is not being drawn, then there is no need to
2424 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2425 if (!mShouldDrawNotificationBackground) {
2426 return;
2427 }
2428
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02002429 final boolean awake = mInterpolatedDarkAmount != 0 || mAmbientState.isDark();
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002430 mScrimController.setExcludedBackgroundArea(
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002431 mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002432 : mCurrentBounds);
Selim Cinek614576e2016-01-20 10:54:09 -08002433 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002434 }
2435
2436 /**
2437 * Update the background bounds to the new desired bounds
2438 */
2439 private void updateBackgroundBounds() {
shawnlin3a2a2e22018-05-04 17:09:50 +08002440 getLocationInWindow(mTempInt2);
2441 mBackgroundBounds.left = mTempInt2[0] + mSidePaddings;
2442 mBackgroundBounds.right = mTempInt2[0] + getWidth() - mSidePaddings;
2443
Selim Cinek614576e2016-01-20 10:54:09 -08002444 if (!mIsExpanded) {
2445 mBackgroundBounds.top = 0;
2446 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002447 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002448 }
2449 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002450 int top = 0;
2451 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002452 // Round Y up to avoid seeing the background during animation
2453 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002454 if (mAnimateNextBackgroundTop
2455 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2456 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002457 // we're ending up at the same location as we are now, lets just skip the animation
2458 top = finalTranslationY;
2459 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002460 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002461 }
2462 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002463 ActivatableNotificationView lastView =
2464 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2465 ? mShelf
2466 : mLastVisibleBackgroundChild;
2467 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002468 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002469 int finalTranslationY;
2470 if (lastView == mShelf) {
2471 finalTranslationY = (int) mShelf.getTranslationY();
2472 } else {
2473 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2474 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002475 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002476 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek614576e2016-01-20 10:54:09 -08002477 if (mAnimateNextBackgroundBottom
2478 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2479 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002480 // we're ending up at the same location as we are now, lets just skip the animation
2481 bottom = finalBottom;
2482 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002483 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2484 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002485 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002486 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002487 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002488 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002489 }
Selim Cinek355652a2016-12-07 13:32:12 -08002490 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002491 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002492 } else {
2493 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002494 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002495 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002496 mBackgroundBounds.top = top;
shawnlinc3457912018-05-15 16:39:56 +08002497 mBackgroundBounds.bottom = Math.max(bottom, top);
Selim Cinek6811d722016-01-19 17:53:12 -08002498 }
2499
Selim Cinek614576e2016-01-20 10:54:09 -08002500 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2501 int childCount = getChildCount();
2502 for (int i = 0; i < childCount; i++) {
2503 View child = getChildAt(i);
2504 if (child.getVisibility() != View.GONE
2505 && child instanceof ExpandableNotificationRow) {
2506 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2507 if (row.isPinned()) {
2508 return row;
2509 }
2510 }
2511 }
2512 return null;
2513 }
2514
2515 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002516 int childCount = getChildCount();
2517 for (int i = childCount - 1; i >= 0; i--) {
2518 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002519 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2520 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002521 return (ActivatableNotificationView) child;
2522 }
2523 }
2524 return null;
2525 }
2526
Selim Cinek614576e2016-01-20 10:54:09 -08002527 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002528 int childCount = getChildCount();
2529 for (int i = 0; i < childCount; i++) {
2530 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002531 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2532 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002533 return (ActivatableNotificationView) child;
2534 }
2535 }
2536 return null;
2537 }
2538
Selim Cinek67b22602014-03-10 15:40:16 +01002539 /**
2540 * Fling the scroll view
2541 *
2542 * @param velocityY The initial velocity in the Y direction. Positive
2543 * numbers mean that the finger/cursor is moving down the screen,
2544 * which means we want to scroll towards the top.
2545 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002546 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002547 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002548 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002549
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002550 float topAmount = getCurrentOverScrollAmount(true);
2551 float bottomAmount = getCurrentOverScrollAmount(false);
2552 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002553 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002554 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002555 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002556 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002557 * mOverflingDistance + topAmount;
2558 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002559 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002560 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002561 mMaxOverScroll = Math.abs(velocityY) / 1000f
2562 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2563 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002564 } else {
2565 // it will be set once we reach the boundary
2566 mMaxOverScroll = 0.0f;
2567 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002568 int minScrollY = Math.max(0, scrollRange);
2569 if (mExpandedInThisMotion) {
2570 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2571 }
Selim Cinek9212de82017-02-06 16:04:28 -08002572 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2573 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002574
Selim Cinek9212de82017-02-06 16:04:28 -08002575 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002576 }
2577 }
2578
Selim Cinek1408eb52014-06-02 14:45:38 +02002579 /**
2580 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2581 * overScroll view (i.e QS).
2582 */
2583 private boolean shouldOverScrollFling(int initialVelocity) {
2584 float topOverScroll = getCurrentOverScrollAmount(true);
2585 return mScrolledToTopOnFirstDown
2586 && !mExpandedInThisMotion
2587 && topOverScroll > mMinTopOverScrollToEscape
2588 && initialVelocity > 0;
2589 }
2590
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002591 /**
2592 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2593 * account.
2594 *
2595 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002596 * @param animate whether to animate the change
2597 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2598 * {@code qsHeight} is the final top padding
2599 */
Jason Monk16ac3772016-02-10 15:39:21 -05002600 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002601 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002602 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002603 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002604 if (topPadding + minStackHeight > getHeight()) {
2605 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002606 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002607 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002608 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002609 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002610 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002611 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002612 }
2613
shawnlin8e4e92c2018-04-12 18:47:24 +08002614 public void setMaxTopPadding(int maxTopPadding) {
2615 mMaxTopPadding = maxTopPadding;
2616 }
2617
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002618 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002619 if (isHeadsUpTransition()) {
2620 return getTopHeadsUpPinnedHeight();
2621 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002622 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002623 }
2624
Julia Reynolds34f14962018-05-03 12:40:20 +00002625 public int getFirstChildIntrinsicHeight() {
2626 final ExpandableView firstChild = getFirstChildNotGone();
2627 int firstChildMinHeight = firstChild != null
2628 ? firstChild.getIntrinsicHeight()
2629 : mEmptyShadeView != null
2630 ? mEmptyShadeView.getIntrinsicHeight()
2631 : mCollapsedSize;
2632 if (mOwnScrollY > 0) {
2633 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2634 }
2635 return firstChildMinHeight;
2636 }
2637
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002638 public float getTopPaddingOverflow() {
2639 return mTopPaddingOverflow;
2640 }
2641
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002642 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002643 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002644 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002645 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002646 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002647 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002648 shelfHeight = mShelf.getIntrinsicHeight();
2649 }
2650 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002651 }
2652
Selim Cinek1408eb52014-06-02 14:45:38 +02002653 private int clampPadding(int desiredPadding) {
2654 return Math.max(desiredPadding, mIntrinsicPadding);
2655 }
2656
Selim Cinekfed1ab62014-06-17 14:10:33 -07002657 private float getRubberBandFactor(boolean onTop) {
2658 if (!onTop) {
2659 return RUBBER_BAND_FACTOR_NORMAL;
2660 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002661 if (mExpandedInThisMotion) {
2662 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002663 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002664 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2665 } else if (mScrolledToTopOnFirstDown) {
2666 return 1.0f;
2667 }
2668 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002669 }
2670
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002671 /**
2672 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2673 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2674 * overscroll view (e.g. expand QS).
2675 */
2676 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002677 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002678 || !mScrolledToTopOnFirstDown;
2679 }
2680
Selim Cinek67b22602014-03-10 15:40:16 +01002681 private void endDrag() {
2682 setIsBeingDragged(false);
2683
2684 recycleVelocityTracker();
2685
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002686 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2687 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2688 }
2689 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2690 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2691 }
Selim Cinek67b22602014-03-10 15:40:16 +01002692 }
2693
Jorim Jaggi56306252014-07-03 00:40:09 +02002694 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2695 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2696 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2697 }
2698
Selim Cinek67b22602014-03-10 15:40:16 +01002699 @Override
2700 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002701 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002702 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002703 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002704 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002705 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2706 }
Selim Cinek67b22602014-03-10 15:40:16 +01002707 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002708 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002709 scrollWantsIt = onInterceptTouchEventScroll(ev);
2710 }
2711 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002712 if (!mIsBeingDragged
2713 && !mExpandingNotification
2714 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002715 && !mOnlyScrollingInThisMotion
2716 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002717 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2718 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002719 // Check if we need to clear any snooze leavebehinds
2720 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002721 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07002722 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2723 && !scrollWantsIt) {
2724 mCheckForLeavebehind = false;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002725 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
2726 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2727 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07002728 }
2729 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2730 mCheckForLeavebehind = true;
2731 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002732 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2733 }
2734
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002735 private void handleEmptySpaceClick(MotionEvent ev) {
2736 switch (ev.getActionMasked()) {
2737 case MotionEvent.ACTION_MOVE:
2738 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2739 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2740 mTouchIsClick = false;
2741 }
2742 break;
2743 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002744 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002745 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2746 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2747 }
2748 break;
2749 }
2750 }
2751
Selim Cinek1408eb52014-06-02 14:45:38 +02002752 private void initDownStates(MotionEvent ev) {
2753 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2754 mExpandedInThisMotion = false;
2755 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002756 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002757 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002758 mTouchIsClick = true;
2759 mInitialTouchX = ev.getX();
2760 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002761 }
Selim Cinek67b22602014-03-10 15:40:16 +01002762 }
2763
Selim Cinekef5127e2015-12-21 16:55:58 -08002764 public void setChildTransferInProgress(boolean childTransferInProgress) {
2765 mChildTransferInProgress = childTransferInProgress;
2766 }
2767
Christoph Studer068f5922014-04-08 17:43:07 -04002768 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002769 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002770 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002771 // we only call our internal methods if this is actually a removal and not just a
2772 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002773 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002774 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002775 }
2776 }
2777
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002778 @Override
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002779 public void cleanUpViewState(View child) {
2780 if (child == mTranslatingParentView) {
2781 mTranslatingParentView = null;
2782 }
2783 mCurrentStackScrollState.removeViewStateForView(child);
2784 }
2785
Selim Cinek197823d2016-03-24 13:06:00 -07002786 @Override
2787 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2788 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2789 if (disallowIntercept) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002790 cancelLongPress();
Selim Cinek197823d2016-03-24 13:06:00 -07002791 }
2792 }
2793
Selim Cineka5703182016-05-11 21:23:16 -04002794 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002795 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002796 // This is only a position change, don't do anything special
2797 return;
2798 }
Selim Cinekd1395642016-04-28 12:22:42 -07002799 ExpandableView expandableView = (ExpandableView) child;
2800 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002801 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002802 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002803 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002804 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002805 if (!mSwipedOutViews.contains(child)
2806 || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002807 container.addTransientView(child, 0);
2808 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002809 }
2810 } else {
2811 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002812 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002813 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002814
Selim Cineke9bad242016-06-15 11:46:37 -07002815 focusNextViewIfFocused(child);
2816 }
2817
2818 private void focusNextViewIfFocused(View view) {
2819 if (view instanceof ExpandableNotificationRow) {
2820 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2821 if (row.shouldRefocusOnDismiss()) {
2822 View nextView = row.getChildAfterViewWhenDismissed();
2823 if (nextView == null) {
2824 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2825 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2826 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002827 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002828 }
2829 if (nextView != null) {
2830 nextView.requestAccessibilityFocus();
2831 }
2832 }
2833 }
2834
Selim Cinekc27437b2014-05-14 10:23:33 +02002835 }
2836
Selim Cinekb5605e52015-02-20 18:21:41 +01002837 private boolean isChildInGroup(View child) {
2838 return child instanceof ExpandableNotificationRow
2839 && mGroupManager.isChildInGroupWithSummary(
2840 ((ExpandableNotificationRow) child).getStatusBarNotification());
2841 }
2842
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002843 /**
2844 * Generate a remove animation for a child view.
2845 *
2846 * @param child The view to generate the remove animation for.
2847 * @return Whether an animation was generated.
2848 */
2849 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002850 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002851 mAddedHeadsUpChildren.remove(child);
2852 return false;
2853 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002854 if (isClickedHeadsUp(child)) {
Selim Cinek9dd0d042018-05-14 18:12:42 -07002855 // An animation is already running, add it transiently
2856 mClearTransientViewsWhenFinished.add((ExpandableView) child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002857 return true;
2858 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002859 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002860 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002861 // Generate Animations
2862 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002863 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002864 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002865 } else {
2866 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002867 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002868 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002869 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002870 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002871 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002872 }
2873
Selim Cinek0fccc722015-07-29 17:04:36 -07002874 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002875 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002876 }
2877
Selim Cineke0890e52015-06-17 11:17:08 -07002878 /**
2879 * Remove a removed child view from the heads up animations if it was just added there
2880 *
2881 * @return whether any child was removed from the list to animate
2882 */
2883 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2884 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002885 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2886 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002887 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002888 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002889 mTmpList.add(eventPair);
2890 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002891 }
2892 }
Selim Cineke0890e52015-06-17 11:17:08 -07002893 if (hasAddEvent) {
2894 // This child was just added lets remove all events.
2895 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002896 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002897 }
2898 mTmpList.clear();
2899 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002900 }
2901
Selim Cinek572bbd42014-04-25 16:43:27 +02002902 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002903 * @param child the child to query
2904 * @return whether a view is not a top level child but a child notification and that group is
2905 * not expanded
2906 */
2907 private boolean isChildInInvisibleGroup(View child) {
2908 if (child instanceof ExpandableNotificationRow) {
2909 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2910 ExpandableNotificationRow groupSummary =
2911 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2912 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002913 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002914 }
2915 }
2916 return false;
2917 }
2918
2919 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002920 * Updates the scroll position when a child was removed
2921 *
2922 * @param removedChild the removed child
2923 */
Selim Cinek61633a82016-01-25 15:54:10 -08002924 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002925 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002926 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2927 int padding;
2928 if (increasedPaddingAmount >= 0) {
2929 padding = (int) NotificationUtils.interpolate(
2930 mPaddingBetweenElements,
2931 mIncreasedPaddingBetweenElements,
2932 increasedPaddingAmount);
2933 } else {
2934 padding = (int) NotificationUtils.interpolate(
2935 0,
2936 mPaddingBetweenElements,
2937 1.0f + increasedPaddingAmount);
2938 }
Selim Cinek61633a82016-01-25 15:54:10 -08002939 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002940 int endPosition = startingPosition + childHeight;
2941 if (endPosition <= mOwnScrollY) {
2942 // This child is fully scrolled of the top, so we have to deduct its height from the
2943 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002944 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002945 } else if (startingPosition < mOwnScrollY) {
2946 // This child is currently being scrolled into, set the scroll position to the start of
2947 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002948 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002949 }
2950 }
2951
Selim Cinekd7c4e002014-07-04 18:36:42 +02002952 private int getIntrinsicHeight(View view) {
2953 if (view instanceof ExpandableView) {
2954 ExpandableView expandableView = (ExpandableView) view;
2955 return expandableView.getIntrinsicHeight();
2956 }
2957 return view.getHeight();
2958 }
2959
Selim Cinek1f624952017-06-08 19:11:50 -07002960 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002961 ExpandableNotificationRow childInGroup = null;
2962 ExpandableNotificationRow requestedRow = null;
2963 if (isChildInGroup(requestedView)) {
2964 // We're asking for a child in a group. Calculate the position of the parent first,
2965 // then within the parent.
2966 childInGroup = (ExpandableNotificationRow) requestedView;
2967 requestedView = requestedRow = childInGroup.getNotificationParent();
2968 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002969 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002970 float previousPaddingRequest = mPaddingBetweenElements;
2971 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002972 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002973 ExpandableView child = (ExpandableView) getChildAt(i);
2974 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002975 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002976 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002977 float padding;
2978 if (increasedPaddingAmount >= 0.0f) {
2979 padding = (int) NotificationUtils.interpolate(
2980 previousPaddingRequest,
2981 mIncreasedPaddingBetweenElements,
2982 increasedPaddingAmount);
2983 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002984 mPaddingBetweenElements,
2985 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002986 increasedPaddingAmount);
2987 } else {
2988 int ownPadding = (int) NotificationUtils.interpolate(
2989 0,
2990 mPaddingBetweenElements,
2991 1.0f + increasedPaddingAmount);
2992 if (previousPaddingAmount > 0.0f) {
2993 padding = (int) NotificationUtils.interpolate(
2994 ownPadding,
2995 mIncreasedPaddingBetweenElements,
2996 previousPaddingAmount);
2997 } else {
2998 padding = ownPadding;
2999 }
3000 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08003001 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08003002 if (position != 0) {
3003 position += padding;
3004 }
3005 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08003006 }
Adrian Roos4a579672016-05-24 16:54:37 -07003007 if (child == requestedView) {
3008 if (requestedRow != null) {
3009 position += requestedRow.getPositionOfChild(childInGroup);
3010 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003011 return position;
3012 }
Selim Cinek61633a82016-01-25 15:54:10 -08003013 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02003014 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003015 }
3016 }
3017 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02003018 }
3019
3020 @Override
Adam Powell6690d012015-06-17 16:41:56 -07003021 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02003022 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01003023 onViewAddedInternal(child);
3024 }
3025
Selim Cinek614576e2016-01-20 10:54:09 -08003026 private void updateFirstAndLastBackgroundViews() {
3027 ActivatableNotificationView firstChild = getFirstChildWithBackground();
3028 ActivatableNotificationView lastChild = getLastChildWithBackground();
3029 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek29aab962018-02-27 17:05:45 -08003030 mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
3031 mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
Selim Cinek614576e2016-01-20 10:54:09 -08003032 } else {
3033 mAnimateNextBackgroundTop = false;
3034 mAnimateNextBackgroundBottom = false;
3035 }
3036 mFirstVisibleBackgroundChild = firstChild;
3037 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08003038 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek29aab962018-02-27 17:05:45 -08003039 mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
3040 mLastVisibleBackgroundChild);
Selim Cinek515b2032017-11-15 10:20:19 -08003041 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003042 }
3043
Selim Cinekb5605e52015-02-20 18:21:41 +01003044 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003045 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02003046 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003047 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003048 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003049 updateChronometerForChild(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003050 }
3051
Selim Cinekd06c41c2015-07-06 14:51:36 -07003052 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04003053 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003054 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04003055 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003056 }
3057 }
3058
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003059 @Override
Selim Cinekd1395642016-04-28 12:22:42 -07003060 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
3061 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003062 }
3063
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003064 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003065 public void notifyGroupChildAdded(View row) {
3066 onViewAddedInternal(row);
3067 }
3068
Jorim Jaggi75c95042014-05-16 19:09:59 +02003069 public void setAnimationsEnabled(boolean animationsEnabled) {
3070 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003071 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003072 if (!animationsEnabled) {
3073 mSwipedOutViews.clear();
3074 mChildrenToRemoveAnimated.clear();
3075 clearTemporaryViewsInGroup(this);
3076 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003077 }
3078
3079 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003080 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003081 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003082 int childCount = getChildCount();
3083 for (int i = 0; i < childCount; i++) {
3084 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003085 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003086 updateAnimationState(running, child);
3087 }
3088 }
3089
Selim Cinek51ae05d2014-09-09 15:51:38 +02003090 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003091 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003092 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003093 }
3094
Selim Cinek2627d722018-01-19 12:16:49 -08003095 @Override
3096 public void setExpandingNotification(ExpandableNotificationRow row) {
3097 mAmbientState.setExpandingNotification(row);
3098 requestChildrenUpdate();
3099 }
3100
3101 @Override
Selim Cinek8875de12018-03-22 10:14:32 -07003102 public void bindRow(ExpandableNotificationRow row) {
Selim Cinekf0c79e12018-05-14 17:17:31 -07003103 row.setHeadsUpAnimatingAwayListener(animatingAway -> {
3104 mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
3105 mHeadsUpAppearanceController.updateHeader(row.getEntry());
3106 });
Selim Cinek8875de12018-03-22 10:14:32 -07003107 }
3108
3109 @Override
Selim Cinek2627d722018-01-19 12:16:49 -08003110 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3111 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3112 requestChildrenUpdate();
3113 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003114
Selim Cinekcab4a602014-09-03 14:47:57 +02003115 private void updateAnimationState(boolean running, View child) {
3116 if (child instanceof ExpandableNotificationRow) {
3117 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3118 row.setIconAnimationRunning(running);
3119 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003120 }
3121
3122 public boolean isAddOrRemoveAnimationPending() {
3123 return mNeedsAnimation
3124 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3125 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003126
3127 @Override
Jorim Jaggif6411742014-08-05 17:10:43 +00003128 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003129 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003130 // Generate Animations
3131 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003132 if (fromMoreCard) {
3133 mFromMoreCardAdditions.add(child);
3134 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003135 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003136 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003137 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003138 mAddedHeadsUpChildren.add(child);
3139 mChildrenToAddAnimated.remove(child);
3140 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003141 }
3142
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003143 @Override
Selim Cinek572bbd42014-04-25 16:43:27 +02003144 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003145 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003146
3147 if (currentIndex == -1) {
3148 boolean isTransient = false;
3149 if (child instanceof ExpandableNotificationRow
3150 && ((ExpandableNotificationRow)child).getTransientContainer() != null) {
3151 isTransient = true;
3152 }
3153 Log.e(TAG, "Attempting to re-position "
3154 + (isTransient ? "transient" : "")
3155 + " view {"
3156 + child
3157 + "}");
3158 return;
3159 }
3160
Dan Sandlereceda3d2014-07-21 15:35:01 -04003161 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003162 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08003163 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003164 removeView(child);
3165 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08003166 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003167 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003168 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003169 mChildrenChangingPositions.add(child);
3170 mNeedsAnimation = true;
3171 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003172 }
3173 }
3174
Selim Cinekf4c19962014-05-01 21:55:31 +02003175 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003176 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003177 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003178 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003179 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003180 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003181 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003182 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
3183 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003184 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003185 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003186 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003187 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003188 } else {
3189 applyCurrentState();
3190 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003191 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003192 }
3193
3194 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003195 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003196 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003197 generateChildAdditionEvents();
3198 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02003199 generateSnapBackEvents();
3200 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003201 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003202 generateActivateEvent();
3203 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003204 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003205 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003206 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003207 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003208 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003209 generateAnimateEverythingEvent();
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07003210 generatePulsingAnimationEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003211 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003212 }
3213
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003214 private void generateHeadsUpAnimationEvents() {
3215 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003216 ExpandableNotificationRow row = eventPair.first;
3217 boolean isHeadsUp = eventPair.second;
3218 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3219 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003220 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003221 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003222 type = row.wasJustClicked()
3223 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3224 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003225 if (row.isChildInGroup()) {
3226 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003227 row.setHeadsUpAnimatingAway(false);
Selim Cinekf93bf3e2018-05-08 14:43:21 -07003228 continue;
Selim Cinek76e813c2016-07-14 11:16:58 -07003229 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003230 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08003231 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04003232 if (viewState == null) {
3233 // A view state was never generated for this view, so we don't need to animate
3234 // this. This may happen with notification children.
3235 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003236 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003237 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3238 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3239 // Our custom add animation
3240 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3241 } else {
3242 // Normal add animation
3243 type = AnimationEvent.ANIMATION_TYPE_ADD;
3244 }
3245 onBottom = !pinnedAndClosed;
3246 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003247 }
3248 AnimationEvent event = new AnimationEvent(row, type);
3249 event.headsUpFromBottom = onBottom;
3250 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003251 }
3252 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003253 mAddedHeadsUpChildren.clear();
3254 }
3255
Selim Cinekbbcebde2016-11-09 18:28:20 -08003256 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003257 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3258 return false;
3259 }
3260 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003261 }
3262
Selim Cinekb5605e52015-02-20 18:21:41 +01003263 private void generateGroupExpansionEvent() {
3264 // Generate a group expansion/collapsing event if there is such a group at all
3265 if (mExpandedGroupView != null) {
3266 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3267 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3268 mExpandedGroupView = null;
3269 }
3270 }
3271
Selim Cineka5e211b2014-08-11 17:35:48 +02003272 private void generateViewResizeEvent() {
3273 if (mNeedViewResizeAnimation) {
3274 mAnimationEvents.add(
3275 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3276 }
3277 mNeedViewResizeAnimation = false;
3278 }
3279
Selim Cinekeb973562014-05-02 17:07:49 +02003280 private void generateSnapBackEvents() {
3281 for (View child : mSnappedBackChildren) {
3282 mAnimationEvents.add(new AnimationEvent(child,
3283 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3284 }
3285 mSnappedBackChildren.clear();
3286 }
3287
3288 private void generateDragEvents() {
3289 for (View child : mDragAnimPendingChildren) {
3290 mAnimationEvents.add(new AnimationEvent(child,
3291 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3292 }
3293 mDragAnimPendingChildren.clear();
3294 }
3295
Selim Cinek572bbd42014-04-25 16:43:27 +02003296 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003297 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003298 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003299
3300 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003301 float removedTranslation = child.getTranslationY();
3302 boolean ignoreChildren = true;
3303 if (child instanceof ExpandableNotificationRow) {
3304 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3305 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3306 removedTranslation = row.getTranslationWhenRemoved();
3307 ignoreChildren = false;
3308 }
Selim Cinek51052042017-07-04 12:07:55 +02003309 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003310 }
Selim Cinek51052042017-07-04 12:07:55 +02003311 if (!childWasSwipedOut) {
3312 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003313 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Rohan Shaha7594962018-05-22 10:59:30 -07003314
3315 if (childWasSwipedOut && child instanceof ExpandableView) {
3316 // Clean up any potential transient views if the child has already been swiped
3317 // out, as we won't be animating it further (due to its height already being
3318 // clipped to 0.
3319 ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
3320 if (transientContainer != null) {
3321 transientContainer.removeTransientView(child);
3322 }
3323 }
Selim Cinek51052042017-07-04 12:07:55 +02003324 }
3325 int animationType = childWasSwipedOut
3326 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3327 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3328 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003329 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3330 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003331 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003332 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003333 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003334 mChildrenToRemoveAnimated.clear();
3335 }
3336
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003337 private void generatePositionChangeEvents() {
3338 for (View child : mChildrenChangingPositions) {
3339 mAnimationEvents.add(new AnimationEvent(child,
3340 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3341 }
3342 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003343 if (mGenerateChildOrderChangedEvent) {
3344 mAnimationEvents.add(new AnimationEvent(null,
3345 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3346 mGenerateChildOrderChangedEvent = false;
3347 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003348 }
3349
Selim Cinek572bbd42014-04-25 16:43:27 +02003350 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003351 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003352 if (mFromMoreCardAdditions.contains(child)) {
3353 mAnimationEvents.add(new AnimationEvent(child,
3354 AnimationEvent.ANIMATION_TYPE_ADD,
3355 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3356 } else {
3357 mAnimationEvents.add(new AnimationEvent(child,
3358 AnimationEvent.ANIMATION_TYPE_ADD));
3359 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003360 }
3361 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003362 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003363 }
3364
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003365 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003366 if (mTopPaddingNeedsAnimation) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003367 AnimationEvent event;
3368 if (mAmbientState.isDark()) {
3369 event = new AnimationEvent(null /* view */,
3370 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
3371 KeyguardSliceView.DEFAULT_ANIM_DURATION);
3372 } else {
3373 event = new AnimationEvent(null /* view */,
3374 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
3375 }
3376 mAnimationEvents.add(event);
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003377 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003378 mTopPaddingNeedsAnimation = false;
3379 }
3380
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003381 private void generateActivateEvent() {
3382 if (mActivateNeedsAnimation) {
3383 mAnimationEvents.add(
3384 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3385 }
3386 mActivateNeedsAnimation = false;
3387 }
3388
Selim Cinekd9acca52014-09-01 22:33:25 +02003389 private void generateAnimateEverythingEvent() {
3390 if (mEverythingNeedsAnimation) {
3391 mAnimationEvents.add(
3392 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3393 }
3394 mEverythingNeedsAnimation = false;
3395 }
3396
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003397 private void generateDimmedEvent() {
3398 if (mDimmedNeedsAnimation) {
3399 mAnimationEvents.add(
3400 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3401 }
3402 mDimmedNeedsAnimation = false;
3403 }
3404
Jorim Jaggiae441282014-08-01 02:45:18 +02003405 private void generateHideSensitiveEvent() {
3406 if (mHideSensitiveNeedsAnimation) {
3407 mAnimationEvents.add(
3408 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3409 }
3410 mHideSensitiveNeedsAnimation = false;
3411 }
3412
John Spurlockbf370992014-06-17 13:58:31 -04003413 private void generateDarkEvent() {
3414 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003415 AnimationEvent ev = new AnimationEvent(null,
3416 AnimationEvent.ANIMATION_TYPE_DARK,
3417 new AnimationFilter()
3418 .animateDark()
3419 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003420 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3421 mAnimationEvents.add(ev);
John Spurlockbf370992014-06-17 13:58:31 -04003422 }
3423 mDarkNeedsAnimation = false;
3424 }
3425
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003426 private void generateGoToFullShadeEvent() {
3427 if (mGoToFullShadeNeedsAnimation) {
3428 mAnimationEvents.add(
3429 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3430 }
3431 mGoToFullShadeNeedsAnimation = false;
3432 }
3433
Selim Cinek67b22602014-03-10 15:40:16 +01003434 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003435 if (!isScrollingEnabled()) {
3436 return false;
3437 }
Selim Cinek67b22602014-03-10 15:40:16 +01003438 /*
3439 * This method JUST determines whether we want to intercept the motion.
3440 * If we return true, onMotionEvent will be called and we do the actual
3441 * scrolling there.
3442 */
3443
3444 /*
3445 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003446 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003447 * motion.
3448 */
3449 final int action = ev.getAction();
3450 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3451 return true;
3452 }
3453
Selim Cinek67b22602014-03-10 15:40:16 +01003454 switch (action & MotionEvent.ACTION_MASK) {
3455 case MotionEvent.ACTION_MOVE: {
3456 /*
3457 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003458 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003459 */
3460
3461 /*
3462 * Locally do absolute value. mLastMotionY is set to the y value
3463 * of the down event.
3464 */
3465 final int activePointerId = mActivePointerId;
3466 if (activePointerId == INVALID_POINTER) {
3467 // If we don't have a valid id, the touch down wasn't on content.
3468 break;
3469 }
3470
3471 final int pointerIndex = ev.findPointerIndex(activePointerId);
3472 if (pointerIndex == -1) {
3473 Log.e(TAG, "Invalid pointerId=" + activePointerId
3474 + " in onInterceptTouchEvent");
3475 break;
3476 }
3477
3478 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003479 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003480 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003481 final int xDiff = Math.abs(x - mDownX);
3482 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003483 setIsBeingDragged(true);
3484 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003485 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003486 initVelocityTrackerIfNotExists();
3487 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003488 }
3489 break;
3490 }
3491
3492 case MotionEvent.ACTION_DOWN: {
3493 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303494 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07003495 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01003496 setIsBeingDragged(false);
3497 recycleVelocityTracker();
3498 break;
3499 }
3500
3501 /*
3502 * Remember location of down touch.
3503 * ACTION_DOWN always refers to pointer index 0.
3504 */
3505 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003506 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003507 mActivePointerId = ev.getPointerId(0);
3508
3509 initOrResetVelocityTracker();
3510 mVelocityTracker.addMovement(ev);
3511 /*
3512 * If being flinged and user touches the screen, initiate drag;
3513 * otherwise don't. mScroller.isFinished should be false when
3514 * being flinged.
3515 */
3516 boolean isBeingDragged = !mScroller.isFinished();
3517 setIsBeingDragged(isBeingDragged);
3518 break;
3519 }
3520
3521 case MotionEvent.ACTION_CANCEL:
3522 case MotionEvent.ACTION_UP:
3523 /* Release the drag */
3524 setIsBeingDragged(false);
3525 mActivePointerId = INVALID_POINTER;
3526 recycleVelocityTracker();
3527 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08003528 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01003529 }
3530 break;
3531 case MotionEvent.ACTION_POINTER_UP:
3532 onSecondaryPointerUp(ev);
3533 break;
3534 }
3535
3536 /*
3537 * The only time we want to intercept motion events is if we are in the
3538 * drag mode.
3539 */
3540 return mIsBeingDragged;
3541 }
3542
Muyuan Li333a4fc2016-04-16 17:13:46 -07003543 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3544 return new StackScrollAlgorithm(context);
3545 }
3546
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003547 /**
3548 * @return Whether the specified motion event is actually happening over the content.
3549 */
3550 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003551 return isInContentBounds(event.getY());
3552 }
3553
3554 /**
3555 * @return Whether a y coordinate is inside the content.
3556 */
3557 public boolean isInContentBounds(float y) {
3558 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003559 }
3560
Selim Cinek67b22602014-03-10 15:40:16 +01003561 private void setIsBeingDragged(boolean isDragged) {
3562 mIsBeingDragged = isDragged;
3563 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003564 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003565 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003566 }
3567 }
3568
3569 @Override
3570 public void onWindowFocusChanged(boolean hasWindowFocus) {
3571 super.onWindowFocusChanged(hasWindowFocus);
3572 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003573 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003574 }
3575 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003576
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003577 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003578 public void clearChildFocus(View child) {
3579 super.clearChildFocus(child);
3580 if (mForcedScroll == child) {
3581 mForcedScroll = null;
3582 }
3583 }
3584
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003585 public void requestDisallowLongPress() {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003586 cancelLongPress();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003587 }
3588
Adrian Roosfa139752016-04-27 09:59:08 -07003589 public void requestDisallowDismiss() {
3590 mDisallowDismissInThisMotion = true;
3591 }
3592
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003593 public void cancelLongPress() {
3594 mSwipeHelper.cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02003595 }
3596
Selim Cinekfab078b2014-03-27 22:45:58 +01003597 @Override
3598 public boolean isScrolledToTop() {
3599 return mOwnScrollY == 0;
3600 }
3601
3602 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003603 public boolean isScrolledToBottom() {
3604 return mOwnScrollY >= getScrollRange();
3605 }
3606
3607 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003608 public View getHostView() {
3609 return this;
3610 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003611
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003612 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003613 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003614 }
3615
Mady Mellorc2dbe492017-03-30 13:22:03 -07003616 public void checkSnoozeLeavebehind() {
3617 if (mCheckForLeavebehind) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09003618 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
3619 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3620 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003621 mCheckForLeavebehind = false;
3622 }
3623 }
3624
3625 public void resetCheckSnoozeLeavebehind() {
3626 mCheckForLeavebehind = true;
3627 }
3628
Selim Cinek1685e632014-04-08 02:27:49 +02003629 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003630 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003631 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003632 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02003633 }
3634
3635 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003636 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07003637 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08003638 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003639 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003640 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003641 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003642 clearTemporaryViews();
3643 clearUserLockedViews();
3644 }
3645 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01003646
Selim Cinek5b1591a2017-07-03 17:05:01 +02003647 private void clearUserLockedViews() {
3648 for (int i = 0; i < getChildCount(); i++) {
3649 ExpandableView child = (ExpandableView) getChildAt(i);
3650 if (child instanceof ExpandableNotificationRow) {
3651 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3652 row.setUserLocked(false);
3653 }
3654 }
3655 }
3656
3657 private void clearTemporaryViews() {
Selim Cinek9dd0d042018-05-14 18:12:42 -07003658 // lets make sure nothing is transient anymore
Rohan Shah8ee53652018-04-05 11:13:50 -07003659 clearTemporaryViewsInGroup(this);
Selim Cinek5b1591a2017-07-03 17:05:01 +02003660 for (int i = 0; i < getChildCount(); i++) {
3661 ExpandableView child = (ExpandableView) getChildAt(i);
3662 if (child instanceof ExpandableNotificationRow) {
3663 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Rohan Shah8ee53652018-04-05 11:13:50 -07003664 clearTemporaryViewsInGroup(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003665 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003666 }
Selim Cinek1685e632014-04-08 02:27:49 +02003667 }
3668
Rohan Shah8ee53652018-04-05 11:13:50 -07003669 private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003670 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003671 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003672 }
3673 }
3674
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003675 public void onPanelTrackingStarted() {
3676 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003677 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003678 }
3679 public void onPanelTrackingStopped() {
3680 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003681 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003682 }
3683
Selim Cinekb24e0a92015-06-09 20:17:30 -07003684 public void resetScrollPosition() {
3685 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003686 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003687 }
3688
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003689 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003690 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003691 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003692 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003693 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003694 if (!mIsExpanded) {
3695 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003696 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08003697 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003698 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003699 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003700 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003701 }
3702 }
3703
3704 private void updateChronometers() {
3705 int childCount = getChildCount();
3706 for (int i = 0; i < childCount; i++) {
3707 updateChronometerForChild(getChildAt(i));
3708 }
3709 }
3710
3711 private void updateChronometerForChild(View child) {
3712 if (child instanceof ExpandableNotificationRow) {
3713 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3714 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003715 }
Selim Cinek1685e632014-04-08 02:27:49 +02003716 }
3717
Jorim Jaggibe565df2014-04-28 17:51:23 +02003718 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003719 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003720 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003721 updateScrollPositionOnExpandInBottom(view);
3722 clampScrollPosition();
Lucas Dupin60661a62018-04-12 10:50:13 -07003723 notifyHeightChangeListener(view, needsAnimation);
Selim Cinekbc243a92016-09-27 16:35:13 -07003724 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3725 ? (ExpandableNotificationRow) view
3726 : null;
3727 if (row != null && (row == mFirstVisibleBackgroundChild
3728 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3729 updateAlgorithmLayoutMinHeight();
3730 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003731 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003732 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003733 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003734 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003735 }
3736
Selim Cineka5e211b2014-08-11 17:35:48 +02003737 @Override
3738 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003739 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003740 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003741 }
3742
Selim Cinekf7a14c02014-07-07 14:01:46 +02003743 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07003744 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02003745 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003746 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003747 if (row.isSummaryWithChildren()) {
3748 return;
3749 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003750 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003751 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003752 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003753 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003754 }
Selim Cinekdb167372016-11-17 15:41:17 -08003755 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Anthony Chen9e05d462017-04-07 10:10:21 -07003756 if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08003757 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3758 }
3759 if (endPosition > layoutEnd) {
3760 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003761 mDisallowScrollingInThisMotion = true;
3762 }
3763 }
3764 }
3765 }
3766
Jorim Jaggibe565df2014-04-28 17:51:23 +02003767 public void setOnHeightChangedListener(
3768 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3769 this.mOnHeightChangedListener = mOnHeightChangedListener;
3770 }
3771
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003772 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3773 mOnEmptySpaceClickListener = listener;
3774 }
3775
Selim Cinek572bbd42014-04-25 16:43:27 +02003776 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003777 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003778 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003779 runAnimationFinishedRunnables();
Selim Cinek9dd0d042018-05-14 18:12:42 -07003780 clearTransient();
Selim Cinek8fc78752016-07-13 14:34:56 -07003781 clearHeadsUpDisappearRunning();
3782 }
3783
3784 private void clearHeadsUpDisappearRunning() {
3785 for (int i = 0; i < getChildCount(); i++) {
3786 View view = getChildAt(i);
3787 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003788 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003789 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003790 if (row.isSummaryWithChildren()) {
3791 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003792 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003793 }
3794 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003795 }
3796 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003797 }
3798
Selim Cinek9dd0d042018-05-14 18:12:42 -07003799 private void clearTransient() {
3800 for (ExpandableView view : mClearTransientViewsWhenFinished) {
3801 StackStateAnimator.removeTransientView(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003802 }
Selim Cinek9dd0d042018-05-14 18:12:42 -07003803 mClearTransientViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003804 }
3805
3806 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003807 for (Runnable runnable : mAnimationFinishedRunnables) {
3808 runnable.run();
3809 }
3810 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003811 }
3812
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003813 /**
3814 * See {@link AmbientState#setDimmed}.
3815 */
3816 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07003817 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003818 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003819 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003820 mDimmedNeedsAnimation = true;
3821 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003822 animateDimmed(dimmed);
3823 } else {
3824 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003825 }
3826 requestChildrenUpdate();
3827 }
3828
Selim Cinek8a9308d2017-08-24 09:31:08 -07003829 @VisibleForTesting
3830 boolean isDimmed() {
3831 return mAmbientState.isDimmed();
3832 }
3833
Selim Cinekd35c2792016-01-21 13:20:57 -08003834 private void setDimAmount(float dimAmount) {
3835 mDimAmount = dimAmount;
3836 updateBackgroundDimming();
3837 }
3838
3839 private void animateDimmed(boolean dimmed) {
3840 if (mDimAnimator != null) {
3841 mDimAnimator.cancel();
3842 }
3843 float target = dimmed ? 1.0f : 0.0f;
3844 if (target == mDimAmount) {
3845 return;
3846 }
3847 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3848 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3849 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3850 mDimAnimator.addListener(mDimEndListener);
3851 mDimAnimator.addUpdateListener(mDimUpdateListener);
3852 mDimAnimator.start();
3853 }
3854
Jorim Jaggiae441282014-08-01 02:45:18 +02003855 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3856 if (hideSensitive != mAmbientState.isHideSensitive()) {
3857 int childCount = getChildCount();
3858 for (int i = 0; i < childCount; i++) {
3859 ExpandableView v = (ExpandableView) getChildAt(i);
3860 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3861 }
3862 mAmbientState.setHideSensitive(hideSensitive);
3863 if (animate && mAnimationsEnabled) {
3864 mHideSensitiveNeedsAnimation = true;
3865 mNeedsAnimation = true;
3866 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08003867 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02003868 requestChildrenUpdate();
3869 }
3870 }
3871
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003872 /**
3873 * See {@link AmbientState#setActivatedChild}.
3874 */
Selim Cineka32ab602014-06-11 15:06:01 +02003875 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003876 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003877 if (mAnimationsEnabled) {
3878 mActivateNeedsAnimation = true;
3879 mNeedsAnimation = true;
3880 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003881 requestChildrenUpdate();
3882 }
3883
Selim Cineka32ab602014-06-11 15:06:01 +02003884 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003885 return mAmbientState.getActivatedChild();
3886 }
3887
Selim Cinek572bbd42014-04-25 16:43:27 +02003888 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003889 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003890 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003891 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02003892 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003893 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003894 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003895 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003896 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003897 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08003898 }
3899
3900 private void updateViewShadows() {
3901 // we need to work around an issue where the shadow would not cast between siblings when
3902 // their z difference is between 0 and 0.1
3903
3904 // Lefts first sort by Z difference
3905 for (int i = 0; i < getChildCount(); i++) {
3906 ExpandableView child = (ExpandableView) getChildAt(i);
3907 if (child.getVisibility() != GONE) {
3908 mTmpSortedChildren.add(child);
3909 }
3910 }
3911 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3912
3913 // Now lets update the shadow for the views
3914 ExpandableView previous = null;
3915 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3916 ExpandableView expandableView = mTmpSortedChildren.get(i);
3917 float translationZ = expandableView.getTranslationZ();
3918 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3919 float diff = otherZ - translationZ;
3920 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3921 // There is no fake shadow to be drawn
3922 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3923 } else {
3924 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003925 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3926 expandableView.setFakeShadowIntensity(
3927 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003928 previous.getOutlineAlpha(), (int) yLocation,
3929 previous.getOutlineTranslation());
3930 }
3931 previous = expandableView;
3932 }
3933
3934 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003935 }
3936
Lucas Dupine17ce522017-07-17 15:45:06 -07003937 /**
3938 * Update colors of "dismiss" and "empty shade" views.
3939 *
3940 * @param lightTheme True if light theme should be used.
3941 */
3942 public void updateDecorViews(boolean lightTheme) {
3943 if (lightTheme == mUsingLightTheme) {
3944 return;
3945 }
3946 mUsingLightTheme = lightTheme;
3947 Context context = new ContextThemeWrapper(mContext,
3948 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
3949 final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04003950 mFooterView.setTextColor(textColor);
Lucas Dupine17ce522017-07-17 15:45:06 -07003951 mEmptyShadeView.setTextColor(textColor);
3952 }
3953
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003954 public void goToFullShade(long delay) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003955 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003956 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003957 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003958 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003959 }
3960
Selim Cinek1408eb52014-06-02 14:45:38 +02003961 public void cancelExpandHelper() {
3962 mExpandHelper.cancel();
3963 }
3964
3965 public void setIntrinsicPadding(int intrinsicPadding) {
3966 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07003967 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02003968 }
3969
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003970 public int getIntrinsicPadding() {
3971 return mIntrinsicPadding;
3972 }
3973
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003974 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003975 * @return the y position of the first notification
3976 */
3977 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003978 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003979 }
3980
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003981 @Override
3982 public boolean shouldDelayChildPressedState() {
3983 return true;
3984 }
3985
Jorim Jaggi457cc352014-06-02 22:47:42 +02003986 /**
John Spurlockbf370992014-06-17 13:58:31 -04003987 * See {@link AmbientState#setDark}.
3988 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003989 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02003990 if (mAmbientState.isDark() == dark) {
3991 return;
3992 }
John Spurlockbf370992014-06-17 13:58:31 -04003993 mAmbientState.setDark(dark);
3994 if (animate && mAnimationsEnabled) {
3995 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003996 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02003997 mNeedsAnimation = true;
Selim Cinek6811d722016-01-19 17:53:12 -08003998 } else {
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08003999 setDarkAmount(dark ? 1f : 0f);
Selim Cinek6811d722016-01-19 17:53:12 -08004000 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08004001 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004002 requestChildrenUpdate();
4003 applyCurrentBackgroundBounds();
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004004 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004005 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04004006 }
4007
Bill Lin278e7d62018-06-13 18:07:15 +08004008 private void updatePanelTranslation() {
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004009 setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
Bill Lin278e7d62018-06-13 18:07:15 +08004010 }
4011
4012 public void setVerticalPanelTranslation(float verticalPanelTranslation) {
4013 mVerticalPanelTranslation = verticalPanelTranslation;
4014 updatePanelTranslation();
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004015 }
4016
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004017 /**
4018 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
4019 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
4020 * {@link #mAmbientState}'s dark mode is toggled.
4021 */
4022 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08004023 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07004024 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004025 }
4026
Lucas Dupind285cf02018-01-18 09:18:23 -08004027 private void setDarkAmount(float darkAmount) {
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004028 setDarkAmount(darkAmount, darkAmount);
4029 }
4030
4031 /**
4032 * Sets the current dark amount.
4033 *
4034 * @param linearDarkAmount The dark amount that follows linear interpoloation in the animation,
4035 * i.e. animates from 0 to 1 or vice-versa in a linear manner.
4036 * @param interpolatedDarkAmount The dark amount that follows the actual interpolation of the
4037 * animation curve.
4038 */
4039 public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
4040 mLinearDarkAmount = linearDarkAmount;
4041 mInterpolatedDarkAmount = interpolatedDarkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -07004042 boolean wasFullyDark = mAmbientState.isFullyDark();
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004043 mAmbientState.setDarkAmount(interpolatedDarkAmount);
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004044 boolean nowFullyDark = mAmbientState.isFullyDark();
4045 if (nowFullyDark != wasFullyDark) {
Lucas Dupin16cfe452018-02-08 13:14:50 -08004046 updateContentHeight();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004047 DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004048 if (nowFullyDark && dozeParameters.shouldControlScreenOff()) {
Lucas Dupinb561eda2018-04-09 17:25:04 -07004049 mShelf.fadeInTranslating();
4050 }
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004051 if (mIconAreaController != null) {
4052 mIconAreaController.setFullyDark(nowFullyDark);
4053 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08004054 }
Lucas Dupin60661a62018-04-12 10:50:13 -07004055 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07004056 updateBackgroundDimming();
Bill Lin278e7d62018-06-13 18:07:15 +08004057 updatePanelTranslation();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004058 requestChildrenUpdate();
Selim Cinek972123d2016-05-03 14:25:58 -07004059 }
4060
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004061 public void notifyDarkAnimationStart(boolean dark) {
4062 // We only swap the scaling factor if we're fully dark or fully awake to avoid
4063 // interpolation issues when playing with the power button.
4064 if (mInterpolatedDarkAmount == 0 || mInterpolatedDarkAmount == 1) {
4065 mBackgroundXFactor = dark ? 1.8f : 1.5f;
4066 mDarkXInterpolator = dark
4067 ? Interpolators.FAST_OUT_SLOW_IN_REVERSE
4068 : Interpolators.FAST_OUT_SLOW_IN;
4069 }
Selim Cinek972123d2016-05-03 14:25:58 -07004070 }
4071
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004072 public long getDarkAnimationDuration(boolean dark) {
Lucas Dupin439bd442018-06-12 15:05:28 -07004073 long duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
4074 // Longer animation when sleeping with more than 1 notification
4075 if (dark && getNotGoneChildCount() > 2) {
4076 duration *= 1.2f;
4077 }
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004078 return duration;
Selim Cinek972123d2016-05-03 14:25:58 -07004079 }
4080
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004081 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07004082 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004083 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4084 }
4085 if (screenLocation.y > getBottomMostNotificationBottom()) {
4086 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
4087 }
4088 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
4089 if (child != null) {
4090 return getNotGoneIndex(child);
4091 } else {
4092 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4093 }
4094 }
4095
4096 private int getNotGoneIndex(View child) {
4097 int count = getChildCount();
4098 int notGoneIndex = 0;
4099 for (int i = 0; i < count; i++) {
4100 View v = getChildAt(i);
4101 if (child == v) {
4102 return notGoneIndex;
4103 }
4104 if (v.getVisibility() != View.GONE) {
4105 notGoneIndex++;
4106 }
4107 }
4108 return -1;
4109 }
4110
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004111 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004112 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004113 if (mFooterView != null) {
4114 index = indexOfChild(mFooterView);
4115 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004116 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004117 mFooterView = footerView;
4118 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004119 }
4120
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004121 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004122 int index = -1;
4123 if (mEmptyShadeView != null) {
4124 index = indexOfChild(mEmptyShadeView);
4125 removeView(mEmptyShadeView);
4126 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004127 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004128 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004129 }
4130
4131 public void updateEmptyShadeView(boolean visible) {
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004132 mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
Lucas Dupinc9274ff2018-05-09 17:40:20 -07004133
4134 int oldTextRes = mEmptyShadeView.getTextResource();
4135 int newTextRes = mStatusBar.areNotificationsHidden()
4136 ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
4137 if (oldTextRes != newTextRes) {
4138 mEmptyShadeView.setText(newTextRes);
4139 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004140 }
4141
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004142 public void updateFooterView(boolean visible, boolean showDismissView) {
4143 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004144 return;
4145 }
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004146 boolean animate = mIsExpanded && mAnimationsEnabled;
4147 mFooterView.setVisible(visible, animate);
4148 mFooterView.setSecondaryVisible(showDismissView, animate);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004149 }
4150
4151 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4152 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004153 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004154 handleDismissAllClipping();
4155 }
4156
4157 private void handleDismissAllClipping() {
4158 final int count = getChildCount();
4159 boolean previousChildWillBeDismissed = false;
4160 for (int i = 0; i < count; i++) {
4161 ExpandableView child = (ExpandableView) getChildAt(i);
4162 if (child.getVisibility() == GONE) {
4163 continue;
4164 }
4165 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4166 child.setMinClipTopAmount(child.getClipTopAmount());
4167 } else {
4168 child.setMinClipTopAmount(0);
4169 }
4170 previousChildWillBeDismissed = canChildBeDismissed(child);
4171 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004172 }
4173
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004174 public boolean isFooterViewNotGone() {
4175 return mFooterView != null
4176 && mFooterView.getVisibility() != View.GONE
4177 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004178 }
4179
Selim Cinek38475442018-05-18 11:11:46 -07004180 public boolean isFooterViewContentVisible() {
4181 return mFooterView != null && mFooterView.isContentVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004182 }
4183
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004184 public int getFooterViewHeight() {
4185 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004186 }
4187
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004188 public int getEmptyShadeViewHeight() {
4189 return mEmptyShadeView.getHeight();
4190 }
4191
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004192 public float getBottomMostNotificationBottom() {
4193 final int count = getChildCount();
4194 float max = 0;
4195 for (int childIdx = 0; childIdx < count; childIdx++) {
4196 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4197 if (child.getVisibility() == GONE) {
4198 continue;
4199 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004200 float bottom = child.getTranslationY() + child.getActualHeight()
4201 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004202 if (bottom > max) {
4203 max = bottom;
4204 }
4205 }
Selim Cinekd2281152015-04-10 14:37:46 -07004206 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004207 }
4208
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004209 public void setStatusBar(StatusBar statusBar) {
4210 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004211 }
4212
Selim Cinekb5605e52015-02-20 18:21:41 +01004213 public void setGroupManager(NotificationGroupManager groupManager) {
4214 this.mGroupManager = groupManager;
4215 }
4216
Selim Cinekd9acca52014-09-01 22:33:25 +02004217 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01004218 requestAnimateEverything();
4219 }
4220
4221 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004222 if (mIsExpanded && mAnimationsEnabled) {
4223 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004224 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004225 requestChildrenUpdate();
4226 }
4227 }
4228
Selim Cinek04fb2582015-06-02 19:58:09 +02004229 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004230 int childCount = getChildCount();
4231 for (int i = childCount - 1; i >= 0; i--) {
4232 ExpandableView child = (ExpandableView) getChildAt(i);
4233 if (child.getVisibility() != View.GONE) {
4234 float childTop = child.getY();
4235 if (childTop > touchY) {
4236 // we are above a notification entirely let's abort
4237 return false;
4238 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004239 boolean belowChild = touchY > childTop + child.getActualHeight()
4240 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004241 if (child == mFooterView) {
4242 if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
Selim Cinekabf60bb2015-02-20 17:36:10 +01004243 touchY - childTop)) {
4244 // We clicked on the dismiss button
4245 return false;
4246 }
4247 } else if (child == mEmptyShadeView) {
4248 // We arrived at the empty shade view, for which we accept all clicks
4249 return true;
4250 } else if (!belowChild){
4251 // We are on a child
4252 return false;
4253 }
4254 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004255 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004256 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004257 }
4258
Selim Cinekb5605e52015-02-20 18:21:41 +01004259 @Override
4260 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004261 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4262 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004263 if (animated) {
4264 mExpandedGroupView = changedRow;
4265 mNeedsAnimation = true;
4266 }
4267 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004268 if (!mGroupExpandedForMeasure) {
4269 onHeightChanged(changedRow, false /* needsAnimation */);
4270 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004271 runAfterAnimationFinished(new Runnable() {
4272 @Override
4273 public void run() {
4274 changedRow.onFinishedExpansionChange();
4275 }
4276 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004277 }
4278
4279 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004280 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004281 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004282 }
4283
Selim Cinekc22fff62016-05-20 12:44:30 -07004284 /** @hide */
4285 @Override
4286 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4287 super.onInitializeAccessibilityEventInternal(event);
4288 event.setScrollable(mScrollable);
4289 event.setScrollX(mScrollX);
4290 event.setScrollY(mOwnScrollY);
4291 event.setMaxScrollX(mScrollX);
4292 event.setMaxScrollY(getScrollRange());
4293 }
4294
4295 @Override
4296 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4297 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004298 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004299 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004300 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004301 info.addAction(
4302 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4303 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4304 }
Selim Cinekef406062016-09-29 17:33:13 -07004305 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004306 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4307 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4308 }
4309 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004310 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4311 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004312 }
4313
4314 /** @hide */
4315 @Override
4316 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4317 if (super.performAccessibilityActionInternal(action, arguments)) {
4318 return true;
4319 }
4320 if (!isEnabled()) {
4321 return false;
4322 }
4323 int direction = -1;
4324 switch (action) {
4325 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4326 // fall through
4327 case android.R.id.accessibilityActionScrollDown:
4328 direction = 1;
4329 // fall through
4330 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4331 // fall through
4332 case android.R.id.accessibilityActionScrollUp:
4333 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004334 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004335 final int targetScrollY = Math.max(0,
4336 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4337 if (targetScrollY != mOwnScrollY) {
4338 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004339 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004340 return true;
4341 }
4342 break;
4343 }
4344 return false;
4345 }
4346
Selim Cinekef5127e2015-12-21 16:55:58 -08004347 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004348 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004349 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004350 }
4351
4352 public void generateChildOrderChangedEvent() {
4353 if (mIsExpanded && mAnimationsEnabled) {
4354 mGenerateChildOrderChangedEvent = true;
4355 mNeedsAnimation = true;
4356 requestChildrenUpdate();
4357 }
4358 }
4359
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004360 @Override
4361 public int getContainerChildCount() {
4362 return getChildCount();
4363 }
4364
4365 @Override
4366 public View getContainerChildAt(int i) {
4367 return getChildAt(i);
4368 }
4369
4370 @Override
4371 public void removeContainerView(View v) {
4372 removeView(v);
4373 }
4374
4375 @Override
4376 public void addContainerView(View v) {
4377 addView(v);
4378 }
4379
Selim Cinek684a4422015-04-15 16:18:39 -07004380 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004381 mAnimationFinishedRunnables.add(runnable);
4382 }
4383
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004384 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004385 mHeadsUpManager = headsUpManager;
4386 mAmbientState.setHeadsUpManager(headsUpManager);
Selim Cinek29aab962018-02-27 17:05:45 -08004387 mHeadsUpManager.addListener(mRoundnessManager);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004388 }
4389
4390 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004391 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004392 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4393 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004394 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004395 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004396 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004397 requestChildrenUpdate();
4398 }
4399 }
4400
4401 public void setShadeExpanded(boolean shadeExpanded) {
4402 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004403 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004404 }
4405
Selim Cineka59ecc32015-04-07 10:51:49 -07004406 /**
4407 * Set the boundary for the bottom heads up position. The heads up will always be above this
4408 * position.
4409 *
4410 * @param height the height of the screen
4411 * @param bottomBarHeight the height of the bar on the bottom
4412 */
4413 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4414 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4415 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004416 requestChildrenUpdate();
4417 }
4418
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004419 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
4420 mTrackingHeadsUp = row != null;
4421 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004422 }
4423
Selim Cinekaac93252015-04-14 20:04:12 -07004424 public void setScrimController(ScrimController scrimController) {
4425 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004426 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004427 }
4428
Selim Cinekbbc580b2015-06-03 14:11:03 +02004429 public void forceNoOverlappingRendering(boolean force) {
4430 mForceNoOverlappingRendering = force;
4431 }
4432
4433 @Override
4434 public boolean hasOverlappingRendering() {
4435 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4436 }
4437
Selim Cinek6811d722016-01-19 17:53:12 -08004438 public void setAnimationRunning(boolean animationRunning) {
4439 if (animationRunning != mAnimationRunning) {
4440 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004441 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004442 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004443 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004444 }
4445 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004446 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004447 }
4448 }
4449
Selim Cinek3776fe02016-02-04 13:32:43 -08004450 public boolean isExpanded() {
4451 return mIsExpanded;
4452 }
4453
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07004454 public void setPulsing(boolean pulsing, boolean animated) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004455 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004456 return;
4457 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004458 mPulsing = pulsing;
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07004459 mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
Selim Cinekebf42342017-07-13 15:46:10 +02004460 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004461 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004462 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004463 updateContentHeight();
Adrian Roosd83e9992017-03-16 15:17:57 -07004464 requestChildrenUpdate();
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07004465 notifyHeightChangeListener(null, animated);
4466 mNeedsAnimation |= animated;
4467 }
4468
4469 private void generatePulsingAnimationEvent() {
4470 if (mNeedingPulseAnimation != null) {
4471 int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
4472 : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
4473 mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
4474 mNeedingPulseAnimation = null;
4475 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004476 }
4477
Selim Cinek07304f5222016-05-19 18:31:36 -07004478 public void setFadingOut(boolean fadingOut) {
4479 if (fadingOut != mFadingOut) {
4480 mFadingOut = fadingOut;
4481 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004482 }
4483 }
4484
Selim Cinek48ff9b42016-11-09 19:31:51 -08004485 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4486 if (mScrimController == null) {
4487 // we're not set up yet.
4488 return;
4489 }
4490 if (parentNotFullyVisible != mParentNotFullyVisible) {
4491 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004492 updateFadingState();
4493 }
4494 }
4495
4496 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004497 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004498 updateSrcDrawing();
4499 }
4500
Selim Cinek31d37b92016-04-26 09:56:42 -07004501 @Override
4502 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4503 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004504 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004505 }
4506
Selim Cinekbc243a92016-09-27 16:35:13 -07004507 public void setQsExpanded(boolean qsExpanded) {
4508 mQsExpanded = qsExpanded;
4509 updateAlgorithmLayoutMinHeight();
Riddle Hsu065c01c2018-05-10 23:14:19 +08004510 updateScrollability();
Selim Cinekbc243a92016-09-27 16:35:13 -07004511 }
4512
shawnlin8e4e92c2018-04-12 18:47:24 +08004513 public void setQsExpansionFraction(float qsExpansionFraction) {
4514 mQsExpansionFraction = qsExpansionFraction;
4515 }
4516
Selim Cinekef406062016-09-29 17:33:13 -07004517 public void setOwnScrollY(int ownScrollY) {
4518 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004519 // We still want to call the normal scrolled changed for accessibility reasons
4520 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004521 mOwnScrollY = ownScrollY;
4522 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004523 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004524 }
4525 }
4526
Selim Cinek281c2022016-10-13 19:14:43 -07004527 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004528 int index = -1;
4529 if (mShelf != null) {
4530 index = indexOfChild(mShelf);
4531 removeView(mShelf);
4532 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004533 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004534 addView(mShelf, index);
4535 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004536 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004537 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004538 }
4539
4540 public NotificationShelf getNotificationShelf() {
4541 return mShelf;
4542 }
4543
Selim Cinekad7fac02016-10-18 17:09:15 -07004544 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4545 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4546 mMaxDisplayedNotifications = maxDisplayedNotifications;
4547 updateContentHeight();
4548 notifyHeightChangeListener(mShelf);
4549 }
4550 }
4551
shawnlin8e4e92c2018-04-12 18:47:24 +08004552 public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
4553 mShouldShowShelfOnly = shouldShowShelfOnly;
4554 updateAlgorithmLayoutMinHeight();
4555 }
4556
Selim Cinek48ff9b42016-11-09 19:31:51 -08004557 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004558 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004559 }
4560
Selim Cinekcafa87f2016-10-26 17:00:17 -07004561 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4562 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4563 updateClipping();
4564 }
4565
4566 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4567 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4568 updateClipping();
4569 }
4570
Selim Cinek355652a2016-12-07 13:32:12 -08004571 public void setStatusBarState(int statusBarState) {
4572 mStatusBarState = statusBarState;
4573 mAmbientState.setStatusBarState(statusBarState);
4574 }
4575
Selim Cinekd5ab6452016-12-08 16:34:00 -08004576 public void setExpandingVelocity(float expandingVelocity) {
4577 mAmbientState.setExpandingVelocity(expandingVelocity);
4578 }
4579
Selim Cinekfcff4c62016-12-27 14:26:06 +01004580 public float getOpeningHeight() {
4581 if (mEmptyShadeView.getVisibility() == GONE) {
4582 return getMinExpansionHeight();
4583 } else {
4584 return getAppearEndPosition();
4585 }
4586 }
4587
4588 public void setIsFullWidth(boolean isFullWidth) {
4589 mAmbientState.setPanelFullWidth(isFullWidth);
4590 }
4591
Selim Cinekec29d342017-05-05 18:31:49 -07004592 public void setUnlockHintRunning(boolean running) {
4593 mAmbientState.setUnlockHintRunning(running);
4594 }
4595
Selim Cinek5cf1d052017-06-01 17:36:46 -07004596 public void setQsCustomizerShowing(boolean isShowing) {
4597 mAmbientState.setQsCustomizerShowing(isShowing);
4598 requestChildrenUpdate();
4599 }
4600
4601 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4602 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4603 }
4604
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004605 public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
4606 mAntiBurnInOffsetX = antiBurnInOffsetX;
Bill Lin278e7d62018-06-13 18:07:15 +08004607 updatePanelTranslation();
Adrian Roosdc747bd2017-06-01 16:09:15 -07004608 }
4609
Selim Cinek707e2072017-06-30 18:32:40 +02004610 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4611 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
shawnlin8e4e92c2018-04-12 18:47:24 +08004612 + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
4613 + " qsExpandFraction=%f]",
Selim Cinek707e2072017-06-30 18:32:40 +02004614 this.getClass().getSimpleName(),
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004615 mPulsing ? "T":"f",
Selim Cinek707e2072017-06-30 18:32:40 +02004616 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4617 getVisibility() == View.VISIBLE ? "visible"
4618 : getVisibility() == View.GONE ? "gone"
4619 : "invisible",
4620 getAlpha(),
shawnlin8e4e92c2018-04-12 18:47:24 +08004621 mAmbientState.getScrollY(),
4622 mMaxTopPadding,
4623 mShouldShowShelfOnly ? "T":"f",
4624 mQsExpansionFraction));
Selim Cinek707e2072017-06-30 18:32:40 +02004625 }
4626
Lucas Dupin16cfe452018-02-08 13:14:50 -08004627 public boolean isFullyDark() {
4628 return mAmbientState.isFullyDark();
4629 }
4630
Selim Cinek7103fd42016-05-09 22:22:33 -04004631 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004632 * Add a listener whenever the expanded height changes. The first value passed as an argument
4633 * is the expanded height and the second one is the appearFraction.
4634 *
4635 * @param listener the listener to notify.
4636 */
4637 public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4638 mExpandedHeightListeners.add(listener);
4639 }
4640
4641 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07004642 * Stop a listener from listening to the expandedHeight.
4643 */
4644 public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4645 mExpandedHeightListeners.remove(listener);
4646 }
4647
Selim Cinekf0c79e12018-05-14 17:17:31 -07004648 public void setHeadsUpAppearanceController(
4649 HeadsUpAppearanceController headsUpAppearanceController) {
4650 mHeadsUpAppearanceController = headsUpAppearanceController;
4651 }
4652
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004653 public void setIconAreaController(NotificationIconAreaController controller) {
4654 mIconAreaController = controller;
4655 }
4656
Selim Cinek60ffea62018-03-22 13:16:44 -07004657 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004658 * A listener that is notified when the empty space below the notifications is clicked on
4659 */
4660 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004661 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004662 }
4663
4664 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004665 * A listener that gets notified when the overscroll at the top has changed.
4666 */
4667 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004668
4669 /**
4670 * Notifies a listener that the overscroll has changed.
4671 *
4672 * @param amount the amount of overscroll, in pixels
4673 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4674 * unrubberbanded motion to directly expand overscroll view (e.g expand
4675 * QS)
4676 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004677 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004678
4679 /**
4680 * Notify a listener that the scroller wants to escape from the scrolling motion and
4681 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4682 *
4683 * @param velocity The velocity that the Scroller had when over flinging
4684 * @param open Should the fling open or close the overscroll view.
4685 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004686 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004687 }
4688
Mady Mellor95d743c2017-01-10 12:05:27 -08004689 private class NotificationSwipeHelper extends SwipeHelper
4690 implements NotificationSwipeActionHelper {
4691 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004692 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004693 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004694
4695 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4696 super(swipeDirection, callback, context);
4697 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004698 mFalsingCheck = new Runnable() {
4699 @Override
4700 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004701 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004702 }
4703 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004704 }
4705
4706 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004707 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004708 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004709 if (mCurrMenuRow != null) {
4710 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004711 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004712 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004713 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004714
Mady Mellor95d743c2017-01-10 12:05:27 -08004715 // Slide back any notifications that might be showing a menu
4716 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004717
4718 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004719 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
4720 mCurrMenuRow = row.createMenu();
4721 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4722 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor89e15ec2017-06-28 17:08:21 -07004723 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004724 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004725 }
4726
4727 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004728 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004729 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004730 if (mCurrMenuRow != null) {
4731 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004732 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004733 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004734
Mady Mellor95d743c2017-01-10 12:05:27 -08004735 @Override
4736 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4737 float translation) {
4738 if (mCurrMenuRow != null) {
4739 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004740 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004741 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004742 }
4743
4744 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004745 public void dismissChild(final View view, float velocity,
4746 boolean useAccelerateInterpolator) {
4747 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004748 if (mIsExpanded) {
4749 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4750 // of the panel early.
Rohan Shah524cf7b2018-03-15 14:40:02 -07004751 handleChildViewDismissed(view);
Selim Cinekd1395642016-04-28 12:22:42 -07004752 }
Eliot Courtney47098cb2017-10-18 17:30:30 +09004753 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
4754 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4755 false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004756 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004757 }
4758
4759 @Override
4760 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004761 super.snapChild(animView, targetLeft, velocity);
4762 onDragCancelled(animView);
4763 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004764 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004765 }
4766 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004767
4768 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004769 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4770 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004771 }
4772
Mady Mellorbd707492017-05-10 17:51:25 -07004773 public boolean isFalseGesture(MotionEvent ev) {
4774 return super.isFalseGesture(ev);
4775 }
4776
Mady Mellor95d743c2017-01-10 12:05:27 -08004777 private void handleMenuCoveredOrDismissed() {
4778 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4779 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004780 }
4781 }
4782
Mady Mellor4b80b102016-01-22 08:03:58 -08004783 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004784 public Animator getViewTranslationAnimator(View v, float target,
4785 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004786 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004787 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4788 } else {
4789 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004790 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004791 }
4792
4793 @Override
4794 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004795 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004796 }
4797
4798 @Override
4799 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004800 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004801 }
4802
Mady Mellor95d743c2017-01-10 12:05:27 -08004803 @Override
4804 public void dismiss(View animView, float velocity) {
4805 dismissChild(animView, velocity,
4806 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4807 }
4808
4809 @Override
4810 public void snap(View animView, float targetLeft, float velocity) {
4811 snapChild(animView, targetLeft, velocity);
4812 }
4813
4814 @Override
4815 public boolean swipedFarEnough(float translation, float viewSize) {
4816 return swipedFarEnough();
4817 }
4818
4819 @Override
4820 public boolean swipedFastEnough(float translation, float velocity) {
4821 return swipedFastEnough();
4822 }
4823
4824 @Override
4825 public float getMinDismissVelocity() {
4826 return getEscapeVelocity();
4827 }
4828
4829 public void onMenuShown(View animView) {
4830 onDragCancelled(animView);
4831
4832 // If we're on the lockscreen we want to false this.
4833 if (isAntiFalsingNeeded()) {
4834 mHandler.removeCallbacks(mFalsingCheck);
4835 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4836 }
4837 }
4838
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004839 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09004840 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004841 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004842 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4843 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004844 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004845 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004846 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004847 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004848 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004849 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004850 if (view != null && !isTouchInView(ev, view)) {
4851 // Touch was outside visible guts / menu notification, close what's visible
Eliot Courtney47098cb2017-10-18 17:30:30 +09004852 mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
4853 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4854 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004855 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004856 }
4857 }
4858
Mady Mellor95d743c2017-01-10 12:05:27 -08004859 public void resetExposedMenuView(boolean animate, boolean force) {
4860 if (mMenuExposedView == null
4861 || (!force && mMenuExposedView == mTranslatingParentView)) {
4862 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004863 return;
4864 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004865 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004866 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004867 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004868 0 /* leftTarget */, null /* updateListener */);
4869 if (anim != null) {
4870 anim.start();
4871 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004872 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
Selim Cinekb2e0f332017-08-18 12:24:38 -07004873 ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
4874 if (!row.isRemoved()) {
4875 row.resetTranslation();
4876 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004877 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004878 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004879 }
4880 }
4881
Mady Mellorc2dbe492017-03-30 13:22:03 -07004882 private boolean isTouchInView(MotionEvent ev, View view) {
4883 if (view == null) {
4884 return false;
4885 }
4886 final int height = (view instanceof ExpandableView)
4887 ? ((ExpandableView) view).getActualHeight()
4888 : view.getHeight();
4889 final int rx = (int) ev.getRawX();
4890 final int ry = (int) ev.getRawY();
4891 view.getLocationOnScreen(mTempInt2);
4892 final int x = mTempInt2[0];
4893 final int y = mTempInt2[1];
4894 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4895 boolean ret = rect.contains(rx, ry);
4896 return ret;
4897 }
4898
Selim Cinek33223572016-02-19 19:32:22 -08004899 private void updateContinuousShadowDrawing() {
4900 boolean continuousShadowUpdate = mAnimationRunning
4901 || !mAmbientState.getDraggedViews().isEmpty();
4902 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4903 if (continuousShadowUpdate) {
4904 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4905 } else {
4906 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4907 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004908 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004909 }
4910 }
4911
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004912 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004913 public void resetExposedMenuView(boolean animate, boolean force) {
4914 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004915 }
4916
4917 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4918 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4919 }
4920
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004921 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004922
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004923 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4924
4925 // ANIMATION_TYPE_ADD
4926 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004927 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004928 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004929 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004930 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004931 .animateZ()
4932 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004933
4934 // ANIMATION_TYPE_REMOVE
4935 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004936 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004937 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004938 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004939 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004940 .animateZ()
4941 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004942
4943 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4944 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004945 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004946 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004947 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004948 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004949 .animateZ()
4950 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004951
4952 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4953 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004954 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004955 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004956 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004957 .animateY()
4958 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004959 .animateZ(),
4960
4961 // ANIMATION_TYPE_START_DRAG
4962 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004963 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004964
4965 // ANIMATION_TYPE_SNAP_BACK
4966 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004967 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004968 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004969
4970 // ANIMATION_TYPE_ACTIVATED_CHILD
4971 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004972 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004973
4974 // ANIMATION_TYPE_DIMMED
4975 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004976 .animateDimmed(),
4977
4978 // ANIMATION_TYPE_CHANGE_POSITION
4979 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004980 .animateAlpha() // maybe the children change positions
4981 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004982 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004983 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004984 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004985 .animateZ(),
4986
4987 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004988 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004989
4990 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4991 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004992 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004993 .animateHeight()
4994 .animateTopInset()
4995 .animateY()
4996 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004997 .animateZ()
4998 .hasDelays(),
4999
5000 // ANIMATION_TYPE_HIDE_SENSITIVE
5001 new AnimationFilter()
5002 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02005003
5004 // ANIMATION_TYPE_VIEW_RESIZE
5005 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005006 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02005007 .animateHeight()
5008 .animateTopInset()
5009 .animateY()
5010 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02005011
Selim Cinekb5605e52015-02-20 18:21:41 +01005012 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
5013 new AnimationFilter()
5014 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005015 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01005016 .animateHeight()
5017 .animateTopInset()
5018 .animateY()
5019 .animateZ(),
5020
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005021 // ANIMATION_TYPE_HEADS_UP_APPEAR
5022 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005023 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005024 .animateHeight()
5025 .animateTopInset()
5026 .animateY()
5027 .animateZ(),
5028
5029 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5030 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005031 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005032 .animateHeight()
5033 .animateTopInset()
5034 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07005035 .animateZ()
5036 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005037
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005038 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5039 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005040 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005041 .animateHeight()
5042 .animateTopInset()
5043 .animateY()
5044 .animateZ()
5045 .hasDelays(),
5046
Selim Cineka59ecc32015-04-07 10:51:49 -07005047 // ANIMATION_TYPE_HEADS_UP_OTHER
5048 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005049 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07005050 .animateHeight()
5051 .animateTopInset()
5052 .animateY()
5053 .animateZ(),
5054
Selim Cinekd9acca52014-09-01 22:33:25 +02005055 // ANIMATION_TYPE_EVERYTHING
5056 new AnimationFilter()
5057 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005058 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02005059 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02005060 .animateDimmed()
5061 .animateHideSensitive()
5062 .animateHeight()
5063 .animateTopInset()
5064 .animateY()
5065 .animateZ(),
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005066
5067 // ANIMATION_TYPE_PULSE_APPEAR
5068 new AnimationFilter()
5069 .animateAlpha()
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005070 .hasDelays()
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005071 .animateY(),
5072
5073 // ANIMATION_TYPE_PULSE_DISAPPEAR
5074 new AnimationFilter()
5075 .animateAlpha()
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005076 .hasDelays()
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005077 .animateY(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005078 };
5079
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005080 static int[] LENGTHS = new int[] {
5081
5082 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005083 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005084
5085 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005086 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005087
5088 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5089 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5090
5091 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5092 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5093
5094 // ANIMATION_TYPE_START_DRAG
5095 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5096
5097 // ANIMATION_TYPE_SNAP_BACK
5098 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5099
5100 // ANIMATION_TYPE_ACTIVATED_CHILD
5101 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
5102
5103 // ANIMATION_TYPE_DIMMED
5104 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005105
5106 // ANIMATION_TYPE_CHANGE_POSITION
5107 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04005108
5109 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07005110 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005111
5112 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5113 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02005114
5115 // ANIMATION_TYPE_HIDE_SENSITIVE
5116 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02005117
5118 // ANIMATION_TYPE_VIEW_RESIZE
5119 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02005120
Selim Cinekb5605e52015-02-20 18:21:41 +01005121 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08005122 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01005123
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005124 // ANIMATION_TYPE_HEADS_UP_APPEAR
5125 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
5126
5127 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5128 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5129
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005130 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5131 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5132
Selim Cineka59ecc32015-04-07 10:51:49 -07005133 // ANIMATION_TYPE_HEADS_UP_OTHER
5134 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5135
Selim Cinekd9acca52014-09-01 22:33:25 +02005136 // ANIMATION_TYPE_EVERYTHING
5137 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005138
5139 // ANIMATION_TYPE_PULSE_APPEAR
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005140 StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR,
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005141
5142 // ANIMATION_TYPE_PULSE_DISAPPEAR
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005143 StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR / 2,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005144 };
5145
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005146 static final int ANIMATION_TYPE_ADD = 0;
5147 static final int ANIMATION_TYPE_REMOVE = 1;
5148 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
5149 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
5150 static final int ANIMATION_TYPE_START_DRAG = 4;
5151 static final int ANIMATION_TYPE_SNAP_BACK = 5;
5152 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
5153 static final int ANIMATION_TYPE_DIMMED = 7;
5154 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04005155 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005156 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02005157 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02005158 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01005159 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005160 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
5161 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005162 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
5163 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
5164 static final int ANIMATION_TYPE_EVERYTHING = 18;
Lucas Dupin3d7ccaf2018-04-02 21:19:23 -07005165 static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
5166 static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005167
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005168 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
5169 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
5170
Selim Cinek572bbd42014-04-25 16:43:27 +02005171 final long eventStartTime;
5172 final View changingView;
5173 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005174 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005175 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005176 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005177 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07005178 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02005179
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005180 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005181 this(view, type, LENGTHS[type]);
5182 }
5183
Adrian Roos28f90c72017-05-08 17:24:26 -07005184 AnimationEvent(View view, int type, AnimationFilter filter) {
5185 this(view, type, LENGTHS[type], filter);
5186 }
5187
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005188 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07005189 this(view, type, length, FILTERS[type]);
5190 }
5191
5192 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02005193 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
5194 changingView = view;
5195 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005196 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07005197 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005198 }
5199
5200 /**
5201 * Combines the length of several animation events into a single value.
5202 *
5203 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005204 * @return The combined length. Depending on the event types, this might be the maximum of
5205 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005206 */
5207 static long combineLength(ArrayList<AnimationEvent> events) {
5208 long length = 0;
5209 int size = events.size();
5210 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005211 AnimationEvent event = events.get(i);
5212 length = Math.max(length, event.length);
5213 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
5214 return event.length;
5215 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005216 }
5217 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02005218 }
5219 }
Selim Cinek67b22602014-03-10 15:40:16 +01005220}