blob: 00d5294e6778de52ca279f043461401ffe96ffcd [file] [log] [blame]
Selim Cinek67b22602014-03-10 15:40:16 +01001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.stack;
18
Julia Reynoldsed1c9af2018-03-21 15:21:09 -040019import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
20 .ExpandAnimationParameters;
Selim Cinek2627d722018-01-19 12:16:49 -080021
Selim Cinek614576e2016-01-20 10:54:09 -080022import android.animation.Animator;
23import android.animation.AnimatorListenerAdapter;
24import android.animation.ObjectAnimator;
25import android.animation.PropertyValuesHolder;
Selim Cinekd35c2792016-01-21 13:20:57 -080026import android.animation.TimeAnimator;
27import android.animation.ValueAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080028import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinek31d37b92016-04-26 09:56:42 -070029import android.annotation.FloatRange;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010030import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010031import android.content.Context;
32import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080033import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010034import android.graphics.Canvas;
Lucas Dupind285cf02018-01-18 09:18:23 -080035import android.graphics.Color;
Selim Cinek67b22602014-03-10 15:40:16 +010036import android.graphics.Paint;
Selim Cinek515b2032017-11-15 10:20:19 -080037import android.graphics.Path;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010038import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080039import android.graphics.PorterDuff;
40import android.graphics.PorterDuffXfermode;
41import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070042import android.os.Bundle;
Mady Mellor4b80b102016-01-22 08:03:58 -080043import android.os.Handler;
Mady Mellor95d743c2017-01-10 12:05:27 -080044import android.service.notification.StatusBarNotification;
Aurimas Liutikasfd52c142018-04-17 09:50:46 -070045import androidx.annotation.NonNull;
46import androidx.annotation.VisibleForTesting;
47import androidx.core.graphics.ColorUtils;
Selim Cinek67b22602014-03-10 15:40:16 +010048import android.util.AttributeSet;
Selim Cinek972123d2016-05-03 14:25:58 -070049import android.util.FloatProperty;
Selim Cinek67b22602014-03-10 15:40:16 +010050import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080051import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070052import android.util.Pair;
Selim Cinek972123d2016-05-03 14:25:58 -070053import android.util.Property;
Lucas Dupine17ce522017-07-17 15:45:06 -070054import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070055import android.view.InputDevice;
Selim Cinek67b22602014-03-10 15:40:16 +010056import android.view.MotionEvent;
57import android.view.VelocityTracker;
58import android.view.View;
59import android.view.ViewConfiguration;
60import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020061import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070062import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070063import android.view.accessibility.AccessibilityEvent;
64import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020065import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080066import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010067import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070068import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070069
Mady Mellora41587b2016-02-11 18:43:06 -080070import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010071import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Lucas Dupin4e023812018-04-02 21:19:23 -070072import com.android.keyguard.KeyguardSliceView;
Lucas Dupine17ce522017-07-17 15:45:06 -070073import com.android.settingslib.Utils;
Rohan Shah524cf7b2018-03-15 14:40:02 -070074import com.android.systemui.Dependency;
Selim Cinek67b22602014-03-10 15:40:16 +010075import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080076import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010077import com.android.systemui.R;
78import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070079import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080080import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
81import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
82import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Selim Cineka32ab602014-06-11 15:06:01 +020083import com.android.systemui.statusbar.ActivatableNotificationView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020084import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010085import com.android.systemui.statusbar.ExpandableNotificationRow;
Jorim Jaggibe565df2014-04-28 17:51:23 +020086import com.android.systemui.statusbar.ExpandableView;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -040087import com.android.systemui.statusbar.FooterView;
Rohan Shah524cf7b2018-03-15 14:40:02 -070088import com.android.systemui.statusbar.NotificationBlockingHelperManager;
Adrian Roos7d062c42017-03-30 15:11:43 -070089import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070090import com.android.systemui.statusbar.NotificationGuts;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090091import com.android.systemui.statusbar.NotificationListContainer;
92import com.android.systemui.statusbar.NotificationLogger;
Selim Cinek0cfbef42016-11-09 19:06:36 -080093import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070094import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010095import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020096import com.android.systemui.statusbar.StatusBarState;
Selim Cinek33223572016-02-19 19:32:22 -080097import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080098import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080099import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Lucas Dupinb561eda2018-04-09 17:25:04 -0700100import com.android.systemui.statusbar.phone.DozeParameters;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700101import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900102import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Selim Cinekb5605e52015-02-20 18:21:41 +0100103import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700104import com.android.systemui.statusbar.phone.NotificationIconAreaController;
Selim Cinekaac93252015-04-14 20:04:12 -0700105import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800106import com.android.systemui.statusbar.phone.StatusBar;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900107import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100108import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +0100109
Selim Cinek707e2072017-06-30 18:32:40 +0200110import java.io.FileDescriptor;
111import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200112import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800113import java.util.Collections;
114import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200115import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800116import java.util.List;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800117import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200118
Selim Cinek67b22602014-03-10 15:40:16 +0100119/**
120 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
121 */
122public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200123 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800124 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900125 NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
126 NotificationListContainer {
Selim Cinek67b22602014-03-10 15:40:16 +0100127
Selim Cinekd35c2792016-01-21 13:20:57 -0800128 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800129 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100130 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200131 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
132 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200133 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100134 /**
135 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
136 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800137 private static final int INVALID_POINTER = -1;
Selim Cinek67b22602014-03-10 15:40:16 +0100138
Selim Cinek1408eb52014-06-02 14:45:38 +0200139 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800140 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200141 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100142 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800143 private final Paint mBackgroundPaint = new Paint();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700144 private final boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100145
Selim Cinekbc243a92016-09-27 16:35:13 -0700146 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100147 private int mOwnScrollY;
148 private int mMaxLayoutHeight;
149
150 private VelocityTracker mVelocityTracker;
151 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100152 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100153 private int mTouchSlop;
154 private int mMinimumVelocity;
155 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100156 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200157 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100158 private boolean mIsBeingDragged;
159 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200160 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700161 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100162 private boolean mTouchIsClick;
163 private float mInitialTouchX;
164 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100165
Selim Cinek67b22602014-03-10 15:40:16 +0100166 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100167 private int mContentHeight;
Lucas Dupin60661a62018-04-12 10:50:13 -0700168 private int mIntrinsicContentHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100169 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100170 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800171 private int mIncreasedPaddingBetweenElements;
shawnlin8e4e92c2018-04-12 18:47:24 +0800172 private int mMaxTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800173 private int mRegularTopPadding;
174 private int mDarkTopPadding;
175 // Current padding, will be either mRegularTopPadding or mDarkTopPadding
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200176 private int mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800177 // Distance between AOD separator and shelf
178 private int mDarkSeparatorPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700179 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700180 private int mBottomInset = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +0800181 private float mQsExpansionFraction;
Selim Cinek67b22602014-03-10 15:40:16 +0100182
183 /**
184 * The algorithm which calculates the properties for our children
185 */
Muyuan Li87798022016-04-07 17:51:25 -0700186 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100187
188 /**
189 * The current State this Layout is in
190 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200191 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700192 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100193 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800194 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700195 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
196 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
197 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
198 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
199 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200200 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700201 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
202 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200203 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200204 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200205 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800206 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200207
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200208 /**
209 * The raw amount of the overScroll on the top, which is not rubber-banded.
210 */
211 private float mOverScrolledTopPixels;
212
213 /**
214 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
215 */
216 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900217 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200218 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200219 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100220 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200221 private boolean mNeedsAnimation;
222 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200223 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200224 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400225 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100226 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200227 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200228 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200229 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200230 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200231 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700232 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200233 private boolean mExpandingNotification;
234 private boolean mExpandedInThisMotion;
shawnlin8e4e92c2018-04-12 18:47:24 +0800235 private boolean mShouldShowShelfOnly;
Muyuan Li84b45612016-06-01 11:05:08 -0700236 protected boolean mScrollingEnabled;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400237 protected FooterView mFooterView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700238 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400239 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700240 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200241
242 /**
243 * Was the scroller scrolled to the top when the down motion was observed?
244 */
245 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200246 /**
247 * The minimal amount of over scroll which is needed in order to switch to the quick settings
248 * when over scrolling on a expanded card.
249 */
250 private float mMinTopOverScrollToEscape;
251 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700252 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200253 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200254 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700255 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200256 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100257 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700258 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700259
Selim Cinek1408eb52014-06-02 14:45:38 +0200260 /**
261 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
262 * This is needed to avoid scrolling too far after the notification was collapsed in the same
263 * motion.
264 */
265 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000266 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800267
Mady Mellor95d743c2017-01-10 12:05:27 -0800268 private NotificationMenuRowPlugin mCurrMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800269 private View mTranslatingParentView;
Mady Mellor95d743c2017-01-10 12:05:27 -0800270 private View mMenuExposedView;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700271 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200272
273 /**
274 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
275 * animating.
276 */
277 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700278 private boolean mDisallowDismissInThisMotion;
Selim Cineka59ecc32015-04-07 10:51:49 -0700279 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700280 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200281 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200282 = new ViewTreeObserver.OnPreDrawListener() {
283 @Override
284 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400285 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200286 updateChildren();
287 mChildrenUpdateRequested = false;
288 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200289 return true;
290 }
291 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500292 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100293 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100294 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700295 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek9dd0d042018-05-14 18:12:42 -0700296 private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700297 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
298 = new HashSet<>();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900299 private HeadsUpManagerPhone mHeadsUpManager;
Selim Cinek29aab962018-02-27 17:05:45 -0800300 private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700301 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700302 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200303 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700304 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700305 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800306 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700307 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800308 = new ViewTreeObserver.OnPreDrawListener() {
309 @Override
310 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700311 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800312 return true;
313 }
314 };
315 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800316 private Rect mStartAnimationRect = new Rect();
317 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800318 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800319 private boolean mAnimateNextBackgroundBottom;
320 private boolean mAnimateNextBackgroundTop;
321 private ObjectAnimator mBottomAnimator = null;
322 private ObjectAnimator mTopAnimator = null;
323 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
324 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800325 private int mBgColor;
326 private float mDimAmount;
327 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800328 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800329 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
330 @Override
331 public void onAnimationEnd(Animator animation) {
332 mDimAnimator = null;
333 }
334 };
335 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
336 = new ValueAnimator.AnimatorUpdateListener() {
337
338 @Override
339 public void onAnimationUpdate(ValueAnimator animation) {
340 setDimAmount((Float) animation.getAnimatedValue());
341 }
342 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700343 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800344 private boolean mContinuousShadowUpdate;
345 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
346 = new ViewTreeObserver.OnPreDrawListener() {
347
348 @Override
349 public boolean onPreDraw() {
350 updateViewShadows();
351 return true;
352 }
353 };
354 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
355 @Override
356 public int compare(ExpandableView view, ExpandableView otherView) {
357 float endY = view.getTranslationY() + view.getActualHeight();
358 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
359 if (endY < otherEndY) {
360 return -1;
361 } else if (endY > otherEndY) {
362 return 1;
363 } else {
364 // The two notifications end at the same location
365 return 0;
366 }
367 }
368 };
Selim Cinek25503252016-03-03 15:31:43 -0800369 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900370 private boolean mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700371 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700372 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800373 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700374 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700375 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400376 private View mForcedScroll;
Lucas Dupin4e023812018-04-02 21:19:23 -0700377 private View mNeedingPulseAnimation;
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -0800378 private float mDarkAmount = 0f;
Lucas Dupin439bd442018-06-12 15:05:28 -0700379
380 /**
381 * How fast the background scales in the X direction as a factor of the Y expansion.
382 */
383 private float mBackgroundXFactor = 1f;
Lucas Dupind285cf02018-01-18 09:18:23 -0800384 private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
385 new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
Selim Cinek972123d2016-05-03 14:25:58 -0700386 @Override
387 public void setValue(NotificationStackScrollLayout object, float value) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800388 object.setDarkAmount(value);
Selim Cinek972123d2016-05-03 14:25:58 -0700389 }
390
391 @Override
392 public Float get(NotificationStackScrollLayout object) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800393 return object.getDarkAmount();
Selim Cinek972123d2016-05-03 14:25:58 -0700394 }
395 };
Lucas Dupinb98ddb42018-05-29 17:26:35 -0700396 private ObjectAnimator mDarkAmountAnimator;
Lucas Dupine17ce522017-07-17 15:45:06 -0700397 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700398 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700399 private boolean mForwardScrollable;
400 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700401 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700402 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800403 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700404 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800405 private boolean mNoAmbient;
406 private final Rect mClipRect = new Rect();
407 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700408 private Rect mRequestedClipBounds;
409 private boolean mInHeadsUpPinnedMode;
410 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800411 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100412 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700413 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800414 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek0fe07392017-11-09 13:26:34 -0800415 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800416 private int mSidePaddings;
Lucas Dupind285cf02018-01-18 09:18:23 -0800417 private final int mSeparatorWidth;
418 private final int mSeparatorThickness;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800419 private final Rect mBackgroundAnimationRect = new Rect();
Lucas Dupin0cd882f2018-01-30 12:19:49 -0800420 private int mAntiBurnInOffsetX;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800421 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
422 private int mHeadsUpInset;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700423 private HeadsUpAppearanceController mHeadsUpAppearanceController;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700424 private NotificationIconAreaController mIconAreaController;
Bill Line60aa1e2018-06-13 18:07:15 +0800425 private float mVerticalPanelTranslation;
Selim Cinek67b22602014-03-10 15:40:16 +0100426
427 public NotificationStackScrollLayout(Context context) {
428 this(context, null);
429 }
430
431 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
432 this(context, attrs, 0);
433 }
434
435 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
436 this(context, attrs, defStyleAttr, 0);
437 }
438
439 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
440 int defStyleRes) {
441 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800442 Resources res = getResources();
443
Selim Cinek281c2022016-10-13 19:14:43 -0700444 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800445 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800446 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
447 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200448 mExpandHelper = new ExpandHelper(getContext(), this,
449 minHeight, maxHeight);
450 mExpandHelper.setEventSource(this);
451 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800452 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Muyuan Li333a4fc2016-04-16 17:13:46 -0700453 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100454 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800455 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800456 mShouldDrawNotificationBackground =
457 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700458 mFadeNotificationsOnDismiss =
459 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Lucas Dupind285cf02018-01-18 09:18:23 -0800460 mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
461 mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
462 mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
Selim Cinek29aab962018-02-27 17:05:45 -0800463 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
464 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800465 addOnExpandedHeightListener(mRoundnessManager::setExpanded);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800466
Rohan Shah524cf7b2018-03-15 14:40:02 -0700467 // Blocking helper manager wants to know the expanded state, update as well.
468 NotificationBlockingHelperManager blockingHelperManager =
469 Dependency.get(NotificationBlockingHelperManager.class);
470 addOnExpandedHeightListener((height, unused) -> {
471 blockingHelperManager.setNotificationShadeExpanded(height);
472 });
473
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800474 updateWillNotDraw();
Lucas Dupind285cf02018-01-18 09:18:23 -0800475 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100476 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100477 mDebugPaint = new Paint();
478 mDebugPaint.setColor(0xffff0000);
479 mDebugPaint.setStrokeWidth(2);
480 mDebugPaint.setStyle(Paint.Style.STROKE);
481 }
482 }
483
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900484 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800485 public NotificationSwipeActionHelper getSwipeActionHelper() {
486 return mSwipeHelper;
487 }
488
Selim Cinek67b22602014-03-10 15:40:16 +0100489 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800490 public void onMenuClicked(View view, int x, int y, MenuItem item) {
491 if (mLongPressListener == null) {
492 return;
493 }
494 if (view instanceof ExpandableNotificationRow) {
495 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800496 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
497 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800498 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800499 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800500 }
501
502 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800503 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700504 if (mTranslatingParentView != null && row == mTranslatingParentView) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800505 mMenuExposedView = null;
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700506 mTranslatingParentView = null;
507 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000508 }
509
510 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800511 public void onMenuShown(View row) {
512 mMenuExposedView = mTranslatingParentView;
513 if (row instanceof ExpandableNotificationRow) {
514 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
515 ((ExpandableNotificationRow) row).getStatusBarNotification()
516 .getPackageName());
517 }
518 mSwipeHelper.onMenuShown(row);
519 }
520
Selim Cinek67b22602014-03-10 15:40:16 +0100521 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800522 if (mShouldDrawNotificationBackground
523 && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
524 drawBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700525 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800526
Selim Cinek67b22602014-03-10 15:40:16 +0100527 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800528 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100529 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800530 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100531 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200532 y = getHeight() - getEmptyBottomMargin();
533 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100534 }
535 }
536
Lucas Dupind285cf02018-01-18 09:18:23 -0800537 private void drawBackground(Canvas canvas) {
538 final int lockScreenLeft = mSidePaddings;
539 final int lockScreenRight = getWidth() - mSidePaddings;
540 final int lockScreenTop = mCurrentBounds.top;
541 final int lockScreenBottom = mCurrentBounds.bottom;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700542 int separatorWidth = 0;
543 int separatorThickness = 0;
544 if (mIconAreaController.hasShelfIconsWhenFullyDark()) {
545 separatorThickness = mSeparatorThickness;
546 separatorWidth = mSeparatorWidth;
547 }
548 final int darkLeft = getWidth() / 2 - separatorWidth / 2;
549 final int darkRight = darkLeft + separatorWidth;
550 final int darkTop = (int) (mRegularTopPadding + separatorThickness / 2f);
551 final int darkBottom = darkTop + separatorThickness;
Lucas Dupind285cf02018-01-18 09:18:23 -0800552
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800553 if (mAmbientState.hasPulsingNotifications()) {
Lucas Dupin16cfe452018-02-08 13:14:50 -0800554 // No divider, we have a notification icon instead
555 } else if (mAmbientState.isFullyDark()) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800556 // Only draw divider on AOD if we actually have notifications
557 if (mFirstVisibleBackgroundChild != null) {
558 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
559 }
Lucas Dupind285cf02018-01-18 09:18:23 -0800560 } else {
Lucas Dupin60661a62018-04-12 10:50:13 -0700561 float inverseDark = 1 - mDarkAmount;
562 float yProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(inverseDark);
563 float xProgress = Interpolators.FAST_OUT_SLOW_IN
Lucas Dupin439bd442018-06-12 15:05:28 -0700564 .getInterpolation(inverseDark * mBackgroundXFactor);
Lucas Dupin60661a62018-04-12 10:50:13 -0700565
Lucas Dupin16cfe452018-02-08 13:14:50 -0800566 mBackgroundAnimationRect.set(
Lucas Dupin60661a62018-04-12 10:50:13 -0700567 (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
568 (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
569 (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
570 (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
Lucas Dupin16cfe452018-02-08 13:14:50 -0800571 if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
572 canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
573 mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
574 mCornerRadius, mCornerRadius, mBackgroundPaint);
575 }
Lucas Dupind285cf02018-01-18 09:18:23 -0800576 }
Lucas Dupin16cfe452018-02-08 13:14:50 -0800577 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800578 }
579
Selim Cinekd35c2792016-01-21 13:20:57 -0800580 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800581 // No need to update the background color if it's not being drawn.
582 if (!mShouldDrawNotificationBackground) {
583 return;
584 }
585
Lucas Dupinb561eda2018-04-09 17:25:04 -0700586 float alpha =
587 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
588 alpha *= 1f - mDarkAmount;
589 // We need to manually blend in the background color.
590 int scrimColor = mScrimController.getBackgroundColor();
591 int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
592
593 // Interpolate between semi-transparent notification panel background color
594 // and white AOD separator.
595 float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(mDarkAmount);
596 int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800597
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100598 if (mCachedBackgroundColor != color) {
599 mCachedBackgroundColor = color;
600 mBackgroundPaint.setColor(color);
601 invalidate();
602 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800603 }
604
Selim Cinek67b22602014-03-10 15:40:16 +0100605 private void initView(Context context) {
606 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100607 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200608 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100609 final ViewConfiguration configuration = ViewConfiguration.get(context);
610 mTouchSlop = configuration.getScaledTouchSlop();
611 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
612 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100613 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700614
615 Resources res = context.getResources();
616 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800617 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700618 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700619 mPaddingBetweenElements = Math.max(1,
620 res.getDimensionPixelSize(R.dimen.notification_divider_height));
621 mIncreasedPaddingBetweenElements =
622 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
623 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200624 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800625 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700626 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800627 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700628 mMinInteractionHeight = res.getDimensionPixelSize(
629 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800630 mCornerRadius = res.getDimensionPixelSize(
631 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800632 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
633 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200634 }
635
Selim Cinek25503252016-03-03 15:31:43 -0800636 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700637 mDrawBackgroundAsSrc = asSrc;
638 updateSrcDrawing();
639 }
640
641 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800642 if (!mShouldDrawNotificationBackground) {
643 return;
644 }
645
Selim Cinek48ff9b42016-11-09 19:31:51 -0800646 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700647 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800648 invalidate();
649 }
650
Selim Cinekaef92ef2014-06-06 18:06:04 +0200651 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700652 notifyHeightChangeListener(view, false /* needsAnimation */);
653 }
654
655 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +0200656 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700657 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200658 }
Selim Cinek67b22602014-03-10 15:40:16 +0100659 }
660
661 @Override
662 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
663 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -0800664
665 int width = MeasureSpec.getSize(widthMeasureSpec);
666 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
667 MeasureSpec.getMode(widthMeasureSpec));
Evan Lairdeb7dbd52018-06-28 13:17:25 -0400668 // Don't constrain the height of the children so we know how big they'd like to be
669 int childHeightSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
670 MeasureSpec.UNSPECIFIED);
671
Selim Cinekfa760d42016-05-10 15:50:53 -0400672 // We need to measure all children even the GONE ones, such that the heights are calculated
673 // correctly as they are used to calculate how many we can fit on the screen.
674 final int size = getChildCount();
675 for (int i = 0; i < size; i++) {
Evan Lairdeb7dbd52018-06-28 13:17:25 -0400676 measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400677 }
Selim Cinek67b22602014-03-10 15:40:16 +0100678 }
679
680 @Override
681 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100682 // we layout all our children centered on the top
683 float centerX = getWidth() / 2.0f;
684 for (int i = 0; i < getChildCount(); i++) {
685 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400686 // We need to layout all children even the GONE ones, such that the heights are
687 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100688 float width = child.getMeasuredWidth();
689 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100690 child.layout((int) (centerX - width / 2.0f),
691 0,
692 (int) (centerX + width / 2.0f),
693 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100694 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200695 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100696 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200697 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200698 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800699 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700700 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100701 }
702
Selim Cinek5bc852a2015-12-21 12:19:09 -0800703 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
704 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200705 mNeedViewResizeAnimation = true;
706 mNeedsAnimation = true;
707 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200708 }
709
Selim Cinekdb167372016-11-17 15:41:17 -0800710 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
711 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800712 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200713 }
714
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900715 @Override
716 public void setChildLocationsChangedListener(
717 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200718 mListener = listener;
719 }
720
Selim Cineka7d4f822016-12-06 14:34:47 -0800721 @Override
722 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
723 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200724 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800725 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200726 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100727 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800728 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100729 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800730 if (row.getVisibility() != View.VISIBLE) {
731 return false;
732 }
733 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200734 }
735
Selim Cinek67b22602014-03-10 15:40:16 +0100736 private void setMaxLayoutHeight(int maxLayoutHeight) {
737 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700738 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200739 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100740 }
741
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200742 private void updateAlgorithmHeightAndPadding() {
Lucas Dupin4e023812018-04-02 21:19:23 -0700743 mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700744 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700745 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700746 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100747 }
748
Selim Cinekbc243a92016-09-27 16:35:13 -0700749 private void updateAlgorithmLayoutMinHeight() {
shawnlinc3457912018-05-15 16:39:56 +0800750 mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800751 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -0700752 }
753
Selim Cinek67b22602014-03-10 15:40:16 +0100754 /**
755 * Updates the children views according to the stack scroll algorithm. Call this whenever
756 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
757 */
758 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800759 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800760 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
761 ? 0
762 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200763 mAmbientState.setScrollY(mOwnScrollY);
764 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200765 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200766 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100767 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200768 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100769 }
770 }
771
Selim Cinekc383fd02016-10-21 15:31:26 -0700772 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700773 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800774 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -0700775 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
776 updateBackground();
777 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700778 }
779
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800780 private void updateClippingToTopRoundedCorner() {
Arthur Hungc0ef5652018-05-22 14:00:42 +0800781 Float clipStart = (float) mTopPadding
782 + mStackTranslation
783 + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800784 Float clipEnd = clipStart + mCornerRadius;
785 boolean first = true;
786 for (int i = 0; i < getChildCount(); i++) {
787 ExpandableView child = (ExpandableView) getChildAt(i);
788 if (child.getVisibility() == GONE) {
789 continue;
790 }
791 float start = child.getTranslationY();
Arthur Hungc0ef5652018-05-22 14:00:42 +0800792 float end = start + child.getActualHeight();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800793 boolean clip = clipStart > start && clipStart < end
794 || clipEnd >= start && clipEnd <= end;
795 clip &= !(first && mOwnScrollY == 0);
Selim Cinekeccf4942018-05-30 09:55:36 -0700796 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
797 : ExpandableView.NO_ROUNDNESS);
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800798 first = false;
799 }
800 }
801
Selim Cinek3776fe02016-02-04 13:32:43 -0800802 private void updateScrollStateForAddedChildren() {
803 if (mChildrenToAddAnimated.isEmpty()) {
804 return;
805 }
806 for (int i = 0; i < getChildCount(); i++) {
807 ExpandableView child = (ExpandableView) getChildAt(i);
808 if (mChildrenToAddAnimated.contains(child)) {
809 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800810 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
811 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
812 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800813 int childHeight = getIntrinsicHeight(child) + padding;
814 if (startingPosition < mOwnScrollY) {
815 // This child starts off screen, so let's keep it offscreen to keep the others visible
816
Selim Cinekef406062016-09-29 17:33:13 -0700817 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800818 }
819 }
820 }
821 clampScrollPosition();
822 }
823
Adrian Roos181385c2016-05-05 17:45:44 -0400824 private void updateForcedScroll() {
825 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
826 || !mForcedScroll.isAttachedToWindow())) {
827 mForcedScroll = null;
828 }
829 if (mForcedScroll != null) {
830 ExpandableView expandableView = (ExpandableView) mForcedScroll;
831 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
832 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700833 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400834
835 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700836
837 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
838 // that it is not visible anymore.
839 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700840 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400841 }
842 }
843 }
844
Selim Cinek319bdc42014-05-01 23:01:58 +0200845 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200846 if (!mChildrenUpdateRequested) {
847 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
848 mChildrenUpdateRequested = true;
849 invalidate();
850 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200851 }
852
Selim Cinek67b22602014-03-10 15:40:16 +0100853 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200854 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100855 }
856
Selim Cinekf7a14c02014-07-07 14:01:46 +0200857 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100858 int scrollRange = getScrollRange();
859 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700860 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100861 }
862 }
863
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200864 public int getTopPadding() {
865 return mTopPadding;
866 }
867
Selim Cinek1408eb52014-06-02 14:45:38 +0200868 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800869 if (mRegularTopPadding != topPadding) {
870 mRegularTopPadding = topPadding;
871 mDarkTopPadding = topPadding + mDarkSeparatorPadding;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800872 mAmbientState.setDarkTopPadding(mDarkTopPadding);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200873 updateAlgorithmHeightAndPadding();
874 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200875 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200876 mTopPaddingNeedsAnimation = true;
877 mNeedsAnimation = true;
878 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200879 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -0700880 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200881 }
882 }
883
884 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700885 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200886 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700887 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200888 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700889 public void setExpandedHeight(float height) {
890 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700891 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800892 int minExpansionHeight = getMinExpansionHeight();
893 if (height < minExpansionHeight) {
894 mClipRect.left = 0;
895 mClipRect.right = getWidth();
896 mClipRect.top = 0;
897 mClipRect.bottom = (int) height;
898 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700899 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800900 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700901 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800902 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200903 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700904 float translationY;
905 float appearEndPosition = getAppearEndPosition();
906 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800907 float appearFraction = 1.0f;
shawnlin5be1f7c2018-05-21 20:50:54 +0800908 boolean appearing = height < appearEndPosition;
909 mAmbientState.setAppearing(appearing);
910 if (!appearing) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700911 translationY = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +0800912 if (mShouldShowShelfOnly) {
913 stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
914 } else if (mQsExpanded) {
915 int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
916 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
917 if (stackStartPosition <= stackEndPosition) {
918 stackHeight = stackEndPosition;
919 } else {
920 stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
921 stackEndPosition, mQsExpansionFraction);
922 }
923 } else {
924 stackHeight = (int) height;
925 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200926 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800927 appearFraction = getAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -0700928 if (appearFraction >= 0) {
929 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
930 appearFraction);
931 } else {
932 // This may happen when pushing up a heads up. We linearly push it up from the
933 // start
934 translationY = height - appearStartPosition + getExpandTranslationStart();
935 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800936 if (isHeadsUpTransition()) {
937 stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
938 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
939 } else {
940 stackHeight = (int) (height - translationY);
941 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200942 }
943 if (stackHeight != mCurrentStackHeight) {
944 mCurrentStackHeight = stackHeight;
945 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200946 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200947 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700948 setStackTranslation(translationY);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800949 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
950 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
951 listener.accept(mExpandedHeight, appearFraction);
952 }
Selim Cinekcafa87f2016-10-26 17:00:17 -0700953 }
954
955 private void setRequestedClipBounds(Rect clipRect) {
956 mRequestedClipBounds = clipRect;
957 updateClipping();
958 }
959
Lucas Dupin60661a62018-04-12 10:50:13 -0700960 /**
961 * Return the height of the content ignoring the footer.
962 */
963 public int getIntrinsicContentHeight() {
964 return mIntrinsicContentHeight;
965 }
966
Selim Cinekcafa87f2016-10-26 17:00:17 -0700967 public void updateClipping() {
Lucas Dupin16cfe452018-02-08 13:14:50 -0800968 boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700969 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
970 && !mHeadsUpAnimatingAway;
971 if (mIsClipped != clipped) {
972 mIsClipped = clipped;
973 updateFadingState();
974 }
Lucas Dupin16cfe452018-02-08 13:14:50 -0800975
976 if (animatingClipping) {
977 setClipBounds(mBackgroundAnimationRect);
978 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700979 setClipBounds(mRequestedClipBounds);
980 } else {
981 setClipBounds(null);
982 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700983 }
984
985 /**
986 * @return The translation at the beginning when expanding.
987 * Measured relative to the resting position.
988 */
989 private float getExpandTranslationStart() {
Selim Cinek5bb958d2018-06-18 19:38:11 -0700990 return -mTopPadding + getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700991 }
992
993 /**
994 * @return the position from where the appear transition starts when expanding.
995 * Measured in absolute height.
996 */
997 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800998 if (isHeadsUpTransition()) {
999 return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -07001000 }
Selim Cinek48ff9b42016-11-09 19:31:51 -08001001 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001002 }
1003
1004 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001005 * @return the height of the top heads up notification when pinned. This is different from the
1006 * intrinsic height, which also includes whether the notification is system expanded and
1007 * is mainly used when dragging down from a heads up notification.
1008 */
1009 private int getTopHeadsUpPinnedHeight() {
1010 NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
1011 if (topEntry == null) {
1012 return 0;
1013 }
1014 ExpandableNotificationRow row = topEntry.row;
1015 if (row.isChildInGroup()) {
1016 final ExpandableNotificationRow groupSummary
1017 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
1018 if (groupSummary != null) {
1019 row = groupSummary;
1020 }
1021 }
1022 return row.getPinnedHeadsUpHeight();
1023 }
1024
1025 /**
Selim Cinek94c2d822016-07-13 18:50:04 -07001026 * @return the position from where the appear transition ends when expanding.
1027 * Measured in absolute height.
1028 */
1029 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -07001030 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -07001031 int notGoneChildCount = getNotGoneChildCount();
Julia Reynolds34f14962018-05-03 12:40:20 +00001032 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001033 if (isHeadsUpTransition()
Selim Cinekebf42342017-07-13 15:46:10 +02001034 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001035 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +01001036 } else {
1037 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001038 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1039 appearPosition += mShelf.getIntrinsicHeight();
1040 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001041 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001042 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001043 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001044 }
1045 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001046 }
1047
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001048 private boolean isHeadsUpTransition() {
1049 return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
1050 && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
1051 }
1052
Selim Cinek94c2d822016-07-13 18:50:04 -07001053 /**
1054 * @param height the height of the panel
1055 * @return the fraction of the appear animation that has been performed
1056 */
1057 public float getAppearFraction(float height) {
1058 float appearEndPosition = getAppearEndPosition();
1059 float appearStartPosition = getAppearStartPosition();
1060 return (height - appearStartPosition)
1061 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001062 }
1063
Selim Cinekd2281152015-04-10 14:37:46 -07001064 public float getStackTranslation() {
1065 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001066 }
1067
Selim Cinekd2281152015-04-10 14:37:46 -07001068 private void setStackTranslation(float stackTranslation) {
1069 if (stackTranslation != mStackTranslation) {
1070 mStackTranslation = stackTranslation;
1071 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001072 requestChildrenUpdate();
1073 }
Selim Cinek67b22602014-03-10 15:40:16 +01001074 }
1075
1076 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001077 * 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 +01001078 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1079 *
1080 * @return either the layout height or the externally defined height, whichever is smaller
1081 */
Selim Cinek343e6e22014-04-11 21:23:30 +02001082 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001083 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1084 }
1085
Selim Cinek816c8e42015-11-19 12:00:45 -08001086 public int getFirstItemMinHeight() {
1087 final ExpandableView firstChild = getFirstChildNotGone();
1088 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001089 }
1090
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001091 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
Dan Sandler4247a5c2014-07-23 15:58:08 -04001092 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +01001093 }
1094
Jason Monk16ac3772016-02-10 15:39:21 -05001095 public void setQsContainer(ViewGroup qsContainer) {
1096 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001097 }
1098
Rohan Shah524cf7b2018-03-15 14:40:02 -07001099 /**
1100 * Handles cleanup after the given {@code view} has been fully swiped out (including
1101 * re-invoking dismiss logic in case the notification has not made its way out yet).
1102 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001103 @Override
Rohan Shah524cf7b2018-03-15 14:40:02 -07001104 public void onChildDismissed(View view) {
1105 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekd1395642016-04-28 12:22:42 -07001106 if (!row.isDismissed()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001107 handleChildViewDismissed(view);
Selim Cinekd1395642016-04-28 12:22:42 -07001108 }
1109 ViewGroup transientContainer = row.getTransientContainer();
1110 if (transientContainer != null) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001111 transientContainer.removeTransientView(view);
Selim Cinekd1395642016-04-28 12:22:42 -07001112 }
1113 }
1114
Rohan Shah524cf7b2018-03-15 14:40:02 -07001115 /**
1116 * Starts up notification dismiss and tells the notification, if any, to remove itself from
1117 * layout.
1118 *
1119 * @param view view (e.g. notification) to dismiss from the layout
1120 */
1121 private void handleChildViewDismissed(View view) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04001122 if (mDismissAllInProgress) {
1123 return;
1124 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001125
1126 boolean isBlockingHelperShown = false;
1127
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001128 setSwipingInProgress(false);
Rohan Shah524cf7b2018-03-15 14:40:02 -07001129 if (mDragAnimPendingChildren.contains(view)) {
1130 // We start the swipe and finish it in the same frame; we don't want a drag animation.
1131 mDragAnimPendingChildren.remove(view);
Selim Cinekeb973562014-05-02 17:07:49 +02001132 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001133 mAmbientState.onDragFinished(view);
Selim Cinek33223572016-02-19 19:32:22 -08001134 updateContinuousShadowDrawing();
Rohan Shah524cf7b2018-03-15 14:40:02 -07001135
1136 if (view instanceof ExpandableNotificationRow) {
1137 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001138 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -07001139 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001140 }
Rohan Shah524cf7b2018-03-15 14:40:02 -07001141 isBlockingHelperShown =
1142 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001143 }
Blazej Magnowski72323322015-07-24 11:49:40 -07001144
Rohan Shah524cf7b2018-03-15 14:40:02 -07001145 if (!isBlockingHelperShown) {
1146 mSwipedOutViews.add(view);
1147 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001148 mFalsingManager.onNotificationDismissed();
1149 if (mFalsingManager.shouldEnforceBouncer()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001150 mStatusBar.executeRunnableDismissingKeyguard(
1151 null,
1152 null /* cancelAction */,
1153 false /* dismissShade */,
1154 true /* afterKeyguardGone */,
1155 false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -07001156 }
Selim Cinekeb973562014-05-02 17:07:49 +02001157 }
1158
1159 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08001160 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001161 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -08001162 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +02001163 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001164 if (mAnimationsEnabled) {
1165 mSnappedBackChildren.add(animView);
1166 mNeedsAnimation = true;
1167 }
Selim Cinekeb973562014-05-02 17:07:49 +02001168 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +02001169 } else {
1170 // We start the swipe and snap back in the same frame, we don't want any animation
1171 mDragAnimPendingChildren.remove(animView);
1172 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001173 if (mCurrMenuRow != null && targetLeft == 0) {
1174 mCurrMenuRow.resetMenu();
1175 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08001176 }
Selim Cinek67b22602014-03-10 15:40:16 +01001177 }
1178
Adrian Roos5d9cc662014-05-28 17:08:13 +02001179 @Override
1180 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Anthony Chen7acbb772017-04-07 16:45:25 -07001181 // Returning true prevents alpha fading.
1182 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +02001183 }
1184
Mady Mellorb0a82462016-04-30 17:31:02 -07001185 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001186 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001187 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001188 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001189 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -08001190 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -07001191 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001192 mDragAnimPendingChildren.add(v);
1193 mNeedsAnimation = true;
1194 }
Selim Cinekeb973562014-05-02 17:07:49 +02001195 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +01001196 }
1197
Selim Cinek684a4422015-04-15 16:18:39 -07001198 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001199 if (v instanceof ExpandableNotificationRow) {
1200 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001201 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001202 }
1203 return false;
1204 }
1205
1206 private boolean isHeadsUp(View v) {
1207 if (v instanceof ExpandableNotificationRow) {
1208 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1209 return row.isHeadsUp();
1210 }
1211 return false;
1212 }
1213
Mady Mellorb0a82462016-04-30 17:31:02 -07001214 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001215 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001216 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001217 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001218 }
1219
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001220 @Override
1221 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001222 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001223 }
1224
Mady Mellor4b80b102016-01-22 08:03:58 -08001225 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001226 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001227 View child = getChildAtPosition(ev.getX(), ev.getY());
1228 if (child instanceof ExpandableNotificationRow) {
1229 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1230 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001231 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001232 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001233 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001234 || (parent.getNotificationChildren().size() == 1
1235 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001236 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001237 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001238 // child notifications so we use the parent of the child. We also do the same
1239 // if we only have a single child.
1240 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001241 }
1242 }
1243 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001244 }
1245
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001246 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1247 getLocationOnScreen(mTempInt2);
1248 float localTouchY = touchY - mTempInt2[1];
1249
1250 ExpandableView closestChild = null;
1251 float minDist = Float.MAX_VALUE;
1252
1253 // find the view closest to the location, accounting for GONE views
1254 final int count = getChildCount();
1255 for (int childIdx = 0; childIdx < count; childIdx++) {
1256 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1257 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001258 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001259 continue;
1260 }
1261 float childTop = slidingChild.getTranslationY();
1262 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001263 float bottom = childTop + slidingChild.getActualHeight()
1264 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001265
1266 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1267 if (dist < minDist) {
1268 closestChild = slidingChild;
1269 minDist = dist;
1270 }
1271 }
1272 return closestChild;
1273 }
1274
Mady Mellorb0a82462016-04-30 17:31:02 -07001275 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001276 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001277 getLocationOnScreen(mTempInt2);
1278 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001279 }
1280
Mady Mellorb0a82462016-04-30 17:31:02 -07001281 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001282 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001283 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1284
1285 }
1286
1287 /**
1288 * Get the child at a certain screen location.
1289 *
1290 * @param touchX the x coordinate
1291 * @param touchY the y coordinate
1292 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1293 * @return the child at the given location.
1294 */
1295 private ExpandableView getChildAtPosition(float touchX, float touchY,
1296 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001297 // find the view under the pointer, accounting for GONE views
1298 final int count = getChildCount();
1299 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001300 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001301 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001302 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001303 continue;
1304 }
Selim Cinek89faff12014-06-19 16:29:04 -07001305 float childTop = slidingChild.getTranslationY();
1306 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001307 float bottom = childTop + slidingChild.getActualHeight()
1308 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001309
1310 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1311 // camera affordance).
1312 int left = 0;
1313 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001314
Selim Cinek34ed7c02017-09-08 15:03:12 -07001315 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001316 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001317 if (slidingChild instanceof ExpandableNotificationRow) {
1318 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001319 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001320 && mHeadsUpManager.getTopEntry().row != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001321 && mGroupManager.getGroupSummary(
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001322 mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
Selim Cinek5bc852a2015-12-21 12:19:09 -08001323 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001324 continue;
1325 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001326 return row.getViewAtPosition(touchY - childTop);
1327 }
Selim Cinek67b22602014-03-10 15:40:16 +01001328 return slidingChild;
1329 }
1330 }
1331 return null;
1332 }
1333
Mady Mellorb0a82462016-04-30 17:31:02 -07001334 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001335 public boolean canChildBeExpanded(View v) {
1336 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001337 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001338 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001339 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001340 }
1341
Chris Wren6abeeb92016-05-26 14:44:38 -04001342 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001343 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001344 public void setUserExpandedChild(View v, boolean userExpanded) {
1345 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001346 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001347 if (userExpanded && onKeyguard()) {
1348 // Due to a race when locking the screen while touching, a notification may be
1349 // expanded even after we went back to keyguard. An example of this happens if
1350 // you click in the empty space while expanding a group.
1351
1352 // We also need to un-user lock it here, since otherwise the content height
1353 // calculated might be wrong. We also can't invert the two calls since
1354 // un-userlocking it will trigger a layout switch in the content view.
1355 row.setUserLocked(false);
1356 updateContentHeight();
1357 notifyHeightChangeListener(row);
1358 return;
1359 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001360 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1361 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001362 }
1363 }
1364
Mady Mellorb0a82462016-04-30 17:31:02 -07001365 @Override
1366 public void setExpansionCancelled(View v) {
1367 if (v instanceof ExpandableNotificationRow) {
1368 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1369 }
1370 }
1371
1372 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001373 public void setUserLockedChild(View v, boolean userLocked) {
1374 if (v instanceof ExpandableNotificationRow) {
1375 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1376 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001377 cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02001378 requestDisallowInterceptTouchEvent(true);
1379 }
1380
1381 @Override
1382 public void expansionStateChanged(boolean isExpanding) {
1383 mExpandingNotification = isExpanding;
1384 if (!mExpandedInThisMotion) {
1385 mMaxScrollAfterExpand = mOwnScrollY;
1386 mExpandedInThisMotion = true;
1387 }
1388 }
1389
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001390 @Override
1391 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001392 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001393 }
1394
Selim Cinek1408eb52014-06-02 14:45:38 +02001395 public void setScrollingEnabled(boolean enable) {
1396 mScrollingEnabled = enable;
1397 }
1398
Adrian Roos181385c2016-05-05 17:45:44 -04001399 public void lockScrollTo(View v) {
1400 if (mForcedScroll == v) {
1401 return;
1402 }
1403 mForcedScroll = v;
1404 scrollTo(v);
1405 }
1406
Ricky Waicd35def2016-05-03 11:07:07 +01001407 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001408 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001409 int positionInLinearLayout = getPositionInLinearLayout(v);
1410 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1411 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001412
Adrian Roos4a579672016-05-24 16:54:37 -07001413 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1414 // that it is not visible anymore.
1415 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001416 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1417 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001418 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001419 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001420 }
Ricky Waicd35def2016-05-03 11:07:07 +01001421 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001422 }
1423
Adrian Roos181385c2016-05-05 17:45:44 -04001424 /**
1425 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1426 * the IME.
1427 */
1428 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1429 return positionInLinearLayout + v.getIntrinsicHeight() +
1430 getImeInset() - getHeight() + getTopPadding();
1431 }
1432
Adrian Roos5153d4a2016-03-22 10:01:56 -07001433 @Override
1434 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001435 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001436
1437 int range = getScrollRange();
1438 if (mOwnScrollY > range) {
1439 // HACK: We're repeatedly getting staggered insets here while the IME is
1440 // animating away. To work around that we'll wait until things have settled.
1441 removeCallbacks(mReclamp);
1442 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001443 } else if (mForcedScroll != null) {
1444 // The scroll was requested before we got the actual inset - in case we need
1445 // to scroll up some more do so now.
1446 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001447 }
1448 return insets;
1449 }
1450
1451 private Runnable mReclamp = new Runnable() {
1452 @Override
1453 public void run() {
1454 int range = getScrollRange();
1455 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1456 mDontReportNextOverScroll = true;
1457 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001458 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001459 }
1460 };
1461
Selim Cinek1408eb52014-06-02 14:45:38 +02001462 public void setExpandingEnabled(boolean enable) {
1463 mExpandHelper.setEnabled(enable);
1464 }
1465
1466 private boolean isScrollingEnabled() {
1467 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001468 }
1469
Mady Mellorb0a82462016-04-30 17:31:02 -07001470 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001471 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001472 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001473 }
1474
Selim Cinek19c8c702014-08-25 22:09:19 +02001475 @Override
1476 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001477 return onKeyguard();
1478 }
1479
1480 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001481 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001482 }
1483
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001484 private void setSwipingInProgress(boolean isSwiped) {
1485 mSwipingInProgress = isSwiped;
1486 if(isSwiped) {
1487 requestDisallowInterceptTouchEvent(true);
1488 }
Selim Cinek67b22602014-03-10 15:40:16 +01001489 }
1490
1491 @Override
1492 protected void onConfigurationChanged(Configuration newConfig) {
1493 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001494 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001495 float densityScale = getResources().getDisplayMetrics().density;
1496 mSwipeHelper.setDensityScale(densityScale);
1497 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1498 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1499 initView(getContext());
1500 }
1501
Dan Sandlereceda3d2014-07-21 15:35:01 -04001502 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001503 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1504 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001505 }
1506
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001507 @Override
Mady Mellor86889c22016-04-18 16:37:06 -07001508 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001509 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001510 // If the child is showing the notification menu snap to that
1511 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001512 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001513 }
1514
Selim Cinek67b22602014-03-10 15:40:16 +01001515 @Override
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001516 public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
1517 return this;
1518 }
1519
1520 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001521 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001522 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1523 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001524 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001525 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001526 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001527 if (isCancelOrUp) {
1528 mExpandHelper.onlyObserveMovements(false);
1529 }
1530 boolean wasExpandingBefore = mExpandingNotification;
1531 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001532 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1533 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001534 dispatchDownEventToScroller(ev);
1535 }
1536 }
Selim Cinek67b22602014-03-10 15:40:16 +01001537 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001538 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1539 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001540 scrollerWantsIt = onScrollTouch(ev);
1541 }
1542 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001543 if (!mIsBeingDragged
1544 && !mExpandingNotification
1545 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001546 && !mOnlyScrollingInThisMotion
1547 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001548 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1549 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001550
1551 // Check if we need to clear any snooze leavebehinds
Eliot Courtney47098cb2017-10-18 17:30:30 +09001552 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07001553 if (guts != null && !isTouchInView(ev, guts)
1554 && guts.getGutsContent() instanceof NotificationSnooze) {
1555 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1556 if ((ns.isExpanded() && isCancelOrUp)
1557 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1558 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1559 // clear it on the next non-horizontal swipe or expand event.
1560 checkSnoozeLeavebehind();
1561 }
1562 }
1563 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1564 mCheckForLeavebehind = true;
1565 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001566 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1567 }
1568
1569 private void dispatchDownEventToScroller(MotionEvent ev) {
1570 MotionEvent downEvent = MotionEvent.obtain(ev);
1571 downEvent.setAction(MotionEvent.ACTION_DOWN);
1572 onScrollTouch(downEvent);
1573 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001574 }
1575
Selim Cinek11e33232016-08-05 15:30:53 -07001576 @Override
1577 public boolean onGenericMotionEvent(MotionEvent event) {
1578 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1579 || mDisallowScrollingInThisMotion) {
1580 return false;
1581 }
1582 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1583 switch (event.getAction()) {
1584 case MotionEvent.ACTION_SCROLL: {
1585 if (!mIsBeingDragged) {
1586 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1587 if (vscroll != 0) {
1588 final int delta = (int) (vscroll * getVerticalScrollFactor());
1589 final int range = getScrollRange();
1590 int oldScrollY = mOwnScrollY;
1591 int newScrollY = oldScrollY - delta;
1592 if (newScrollY < 0) {
1593 newScrollY = 0;
1594 } else if (newScrollY > range) {
1595 newScrollY = range;
1596 }
1597 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001598 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001599 return true;
1600 }
1601 }
1602 }
1603 }
1604 }
1605 }
1606 return super.onGenericMotionEvent(event);
1607 }
1608
Selim Cinek67b22602014-03-10 15:40:16 +01001609 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001610 if (!isScrollingEnabled()) {
1611 return false;
1612 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001613 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001614 return false;
1615 }
Adrian Roos181385c2016-05-05 17:45:44 -04001616 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001617 initVelocityTrackerIfNotExists();
1618 mVelocityTracker.addMovement(ev);
1619
1620 final int action = ev.getAction();
1621
1622 switch (action & MotionEvent.ACTION_MASK) {
1623 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001624 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001625 return false;
1626 }
1627 boolean isBeingDragged = !mScroller.isFinished();
1628 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001629 /*
1630 * If being flinged and user touches, stop the fling. isFinished
1631 * will be false if being flinged.
1632 */
1633 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001634 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001635 }
1636
1637 // Remember where the motion event started
1638 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001639 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001640 mActivePointerId = ev.getPointerId(0);
1641 break;
1642 }
1643 case MotionEvent.ACTION_MOVE:
1644 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1645 if (activePointerIndex == -1) {
1646 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1647 break;
1648 }
1649
1650 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001651 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001652 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001653 final int xDiff = Math.abs(x - mDownX);
1654 final int yDiff = Math.abs(deltaY);
1655 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001656 setIsBeingDragged(true);
1657 if (deltaY > 0) {
1658 deltaY -= mTouchSlop;
1659 } else {
1660 deltaY += mTouchSlop;
1661 }
1662 }
1663 if (mIsBeingDragged) {
1664 // Scroll to follow the motion event
1665 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001666 int range = getScrollRange();
1667 if (mExpandedInThisMotion) {
1668 range = Math.min(range, mMaxScrollAfterExpand);
1669 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001670
1671 float scrollAmount;
1672 if (deltaY < 0) {
1673 scrollAmount = overScrollDown(deltaY);
1674 } else {
1675 scrollAmount = overScrollUp(deltaY, range);
1676 }
Selim Cinek67b22602014-03-10 15:40:16 +01001677
Selim Cinek9212de82017-02-06 16:04:28 -08001678 // Calling customOverScrollBy will call onCustomOverScrolled, which
1679 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001680 if (scrollAmount != 0.0f) {
1681 // The scrolling motion could not be compensated with the
1682 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001683 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1684 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001685 // If we're scrolling, leavebehinds should be dismissed
1686 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001687 }
Selim Cinek67b22602014-03-10 15:40:16 +01001688 }
1689 break;
1690 case MotionEvent.ACTION_UP:
1691 if (mIsBeingDragged) {
1692 final VelocityTracker velocityTracker = mVelocityTracker;
1693 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1694 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1695
Selim Cinek1408eb52014-06-02 14:45:38 +02001696 if (shouldOverScrollFling(initialVelocity)) {
1697 onOverScrollFling(true, initialVelocity);
1698 } else {
1699 if (getChildCount() > 0) {
1700 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1701 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1702 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1703 fling(-initialVelocity);
1704 } else {
1705 onOverScrollFling(false, initialVelocity);
1706 }
1707 } else {
1708 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1709 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001710 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001711 }
Selim Cinek67b22602014-03-10 15:40:16 +01001712 }
1713 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001714 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001715 mActivePointerId = INVALID_POINTER;
1716 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001717 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001718
Selim Cinek67b22602014-03-10 15:40:16 +01001719 break;
1720 case MotionEvent.ACTION_CANCEL:
1721 if (mIsBeingDragged && getChildCount() > 0) {
1722 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001723 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001724 }
1725 mActivePointerId = INVALID_POINTER;
1726 endDrag();
1727 }
1728 break;
1729 case MotionEvent.ACTION_POINTER_DOWN: {
1730 final int index = ev.getActionIndex();
1731 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001732 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001733 mActivePointerId = ev.getPointerId(index);
1734 break;
1735 }
1736 case MotionEvent.ACTION_POINTER_UP:
1737 onSecondaryPointerUp(ev);
1738 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001739 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001740 break;
1741 }
1742 return true;
1743 }
1744
Muyuan Li84b45612016-06-01 11:05:08 -07001745 protected boolean isInsideQsContainer(MotionEvent ev) {
1746 return ev.getY() < mQsContainer.getBottom();
1747 }
1748
Selim Cinek1408eb52014-06-02 14:45:38 +02001749 private void onOverScrollFling(boolean open, int initialVelocity) {
1750 if (mOverscrollTopChangedListener != null) {
1751 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1752 }
1753 mDontReportNextOverScroll = true;
1754 setOverScrollAmount(0.0f, true, false);
1755 }
1756
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001757 /**
1758 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1759 *
1760 * @param deltaY The amount to scroll upwards, has to be positive.
1761 * @return The amount of scrolling to be performed by the scroller,
1762 * not handled by the overScroll amount.
1763 */
1764 private float overScrollUp(int deltaY, int range) {
1765 deltaY = Math.max(deltaY, 0);
1766 float currentTopAmount = getCurrentOverScrollAmount(true);
1767 float newTopAmount = currentTopAmount - deltaY;
1768 if (currentTopAmount > 0) {
1769 setOverScrollAmount(newTopAmount, true /* onTop */,
1770 false /* animate */);
1771 }
1772 // Top overScroll might not grab all scrolling motion,
1773 // we have to scroll as well.
1774 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1775 float newScrollY = mOwnScrollY + scrollAmount;
1776 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001777 if (!mExpandedInThisMotion) {
1778 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1779 // We overScroll on the top
1780 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1781 false /* onTop */,
1782 false /* animate */);
1783 }
Selim Cinekef406062016-09-29 17:33:13 -07001784 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001785 scrollAmount = 0.0f;
1786 }
1787 return scrollAmount;
1788 }
1789
1790 /**
1791 * Perform a scroll downward and adapt the overscroll amounts accordingly
1792 *
1793 * @param deltaY The amount to scroll downwards, has to be negative.
1794 * @return The amount of scrolling to be performed by the scroller,
1795 * not handled by the overScroll amount.
1796 */
1797 private float overScrollDown(int deltaY) {
1798 deltaY = Math.min(deltaY, 0);
1799 float currentBottomAmount = getCurrentOverScrollAmount(false);
1800 float newBottomAmount = currentBottomAmount + deltaY;
1801 if (currentBottomAmount > 0) {
1802 setOverScrollAmount(newBottomAmount, false /* onTop */,
1803 false /* animate */);
1804 }
1805 // Bottom overScroll might not grab all scrolling motion,
1806 // we have to scroll as well.
1807 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1808 float newScrollY = mOwnScrollY + scrollAmount;
1809 if (newScrollY < 0) {
1810 float currentTopPixels = getCurrentOverScrolledPixels(true);
1811 // We overScroll on the top
1812 setOverScrolledPixels(currentTopPixels - newScrollY,
1813 true /* onTop */,
1814 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001815 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001816 scrollAmount = 0.0f;
1817 }
1818 return scrollAmount;
1819 }
1820
Selim Cinek67b22602014-03-10 15:40:16 +01001821 private void onSecondaryPointerUp(MotionEvent ev) {
1822 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1823 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1824 final int pointerId = ev.getPointerId(pointerIndex);
1825 if (pointerId == mActivePointerId) {
1826 // This was our active pointer going up. Choose a new
1827 // active pointer and adjust accordingly.
1828 // TODO: Make this decision more intelligent.
1829 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1830 mLastMotionY = (int) ev.getY(newPointerIndex);
1831 mActivePointerId = ev.getPointerId(newPointerIndex);
1832 if (mVelocityTracker != null) {
1833 mVelocityTracker.clear();
1834 }
1835 }
1836 }
1837
1838 private void initVelocityTrackerIfNotExists() {
1839 if (mVelocityTracker == null) {
1840 mVelocityTracker = VelocityTracker.obtain();
1841 }
1842 }
1843
1844 private void recycleVelocityTracker() {
1845 if (mVelocityTracker != null) {
1846 mVelocityTracker.recycle();
1847 mVelocityTracker = null;
1848 }
1849 }
1850
1851 private void initOrResetVelocityTracker() {
1852 if (mVelocityTracker == null) {
1853 mVelocityTracker = VelocityTracker.obtain();
1854 } else {
1855 mVelocityTracker.clear();
1856 }
1857 }
1858
Ricky Waicd35def2016-05-03 11:07:07 +01001859 public void setFinishScrollingCallback(Runnable runnable) {
1860 mFinishScrollingCallback = runnable;
1861 }
1862
Selim Cinek9212de82017-02-06 16:04:28 -08001863 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001864 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001865 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001866 int y = mScroller.getCurrY();
1867
Selim Cinek9212de82017-02-06 16:04:28 -08001868 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001869 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001870 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1871 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001872 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001873 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1874 }
1875 }
Selim Cinek67b22602014-03-10 15:40:16 +01001876
Adrian Roos5153d4a2016-03-22 10:01:56 -07001877 if (mDontClampNextScroll) {
1878 range = Math.max(range, oldY);
1879 }
Selim Cinek9212de82017-02-06 16:04:28 -08001880 customOverScrollBy(y - oldY, oldY, range,
1881 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001882 }
1883
Selim Cinek9212de82017-02-06 16:04:28 -08001884 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001885 } else {
1886 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001887 if (mFinishScrollingCallback != null) {
1888 mFinishScrollingCallback.run();
1889 }
Selim Cinek67b22602014-03-10 15:40:16 +01001890 }
1891 }
1892
Selim Cinek9212de82017-02-06 16:04:28 -08001893 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1894 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001895
1896 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001897 final int top = -maxOverScrollY;
1898 final int bottom = maxOverScrollY + scrollRangeY;
1899
1900 boolean clampedY = false;
1901 if (newScrollY > bottom) {
1902 newScrollY = bottom;
1903 clampedY = true;
1904 } else if (newScrollY < top) {
1905 newScrollY = top;
1906 clampedY = true;
1907 }
1908
Selim Cinek9212de82017-02-06 16:04:28 -08001909 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001910
1911 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001912 }
1913
1914 /**
1915 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1916 * overscroll effect based on numPixels. By default this will also cancel animations on the
1917 * same overScroll edge.
1918 *
1919 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1920 * the rubber-banding logic.
1921 * @param onTop Should the effect be applied on top of the scroller.
1922 * @param animate Should an animation be performed.
1923 */
1924 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001925 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001926 }
1927
1928 /**
1929 * Set the effective overScroll amount which will be directly reflected in the layout.
1930 * By default this will also cancel animations on the same overScroll edge.
1931 *
1932 * @param amount The amount to overScroll by.
1933 * @param onTop Should the effect be applied on top of the scroller.
1934 * @param animate Should an animation be performed.
1935 */
1936 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1937 setOverScrollAmount(amount, onTop, animate, true);
1938 }
1939
1940 /**
1941 * Set the effective overScroll amount which will be directly reflected in the layout.
1942 *
1943 * @param amount The amount to overScroll by.
1944 * @param onTop Should the effect be applied on top of the scroller.
1945 * @param animate Should an animation be performed.
1946 * @param cancelAnimators Should running animations be cancelled.
1947 */
1948 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1949 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001950 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1951 }
1952
1953 /**
1954 * Set the effective overScroll amount which will be directly reflected in the layout.
1955 *
1956 * @param amount The amount to overScroll by.
1957 * @param onTop Should the effect be applied on top of the scroller.
1958 * @param animate Should an animation be performed.
1959 * @param cancelAnimators Should running animations be cancelled.
1960 * @param isRubberbanded The value which will be passed to
1961 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1962 */
1963 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1964 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001965 if (cancelAnimators) {
1966 mStateAnimator.cancelOverScrollAnimators(onTop);
1967 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001968 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001969 }
1970
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001971 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1972 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001973 amount = Math.max(0, amount);
1974 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001975 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001976 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001977 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001978 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001979 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001980 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001981 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001982 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001983 }
1984 }
1985
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001986 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001987 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1988 if (mDontReportNextOverScroll) {
1989 mDontReportNextOverScroll = false;
1990 return;
1991 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001992 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001993 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001994 }
1995 }
1996
1997 public void setOverscrollTopChangedListener(
1998 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1999 mOverscrollTopChangedListener = overscrollTopChangedListener;
2000 }
2001
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002002 public float getCurrentOverScrollAmount(boolean top) {
2003 return mAmbientState.getOverScrollAmount(top);
2004 }
2005
2006 public float getCurrentOverScrolledPixels(boolean top) {
2007 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
2008 }
2009
2010 private void setOverScrolledPixels(float amount, boolean onTop) {
2011 if (onTop) {
2012 mOverScrolledTopPixels = amount;
2013 } else {
2014 mOverScrolledBottomPixels = amount;
2015 }
2016 }
2017
Selim Cinek9212de82017-02-06 16:04:28 -08002018 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002019 // Treat animating scrolls differently; see #computeScroll() for why.
2020 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07002021 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002022 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002023 springBack();
2024 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02002025 float overScrollTop = getCurrentOverScrollAmount(true);
2026 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002027 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002028 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002029 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002030 }
Selim Cinek67b22602014-03-10 15:40:16 +01002031 }
Selim Cinek67b22602014-03-10 15:40:16 +01002032 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08002033 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002034 }
2035 }
2036
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002037 private void springBack() {
2038 int scrollRange = getScrollRange();
2039 boolean overScrolledTop = mOwnScrollY <= 0;
2040 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2041 if (overScrolledTop || overScrolledBottom) {
2042 boolean onTop;
2043 float newAmount;
2044 if (overScrolledTop) {
2045 onTop = true;
2046 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07002047 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02002048 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002049 } else {
2050 onTop = false;
2051 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07002052 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002053 }
2054 setOverScrollAmount(newAmount, onTop, false);
2055 setOverScrollAmount(0.0f, onTop, true);
2056 mScroller.forceFinished(true);
2057 }
2058 }
2059
Selim Cinek67b22602014-03-10 15:40:16 +01002060 private int getScrollRange() {
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002061 int scrollRange = Math.max(0, mContentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002062 int imeInset = getImeInset();
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002063 scrollRange += Math.min(imeInset, Math.max(0, mContentHeight - (getHeight() - imeInset)));
Selim Cineka424c502016-04-05 13:07:54 -07002064 return scrollRange;
2065 }
2066
2067 private int getImeInset() {
2068 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002069 }
2070
Selim Cinek343e6e22014-04-11 21:23:30 +02002071 /**
2072 * @return the first child which has visibility unequal to GONE
2073 */
Selim Cinekb55386d2015-12-16 17:26:49 -08002074 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002075 int childCount = getChildCount();
2076 for (int i = 0; i < childCount; i++) {
2077 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002078 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002079 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002080 }
2081 }
2082 return null;
2083 }
2084
Selim Cinek4a1ac842014-05-01 15:51:58 +02002085 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002086 * @return the child before the given view which has visibility unequal to GONE
2087 */
2088 public ExpandableView getViewBeforeView(ExpandableView view) {
2089 ExpandableView previousView = null;
2090 int childCount = getChildCount();
2091 for (int i = 0; i < childCount; i++) {
2092 View child = getChildAt(i);
2093 if (child == view) {
2094 return previousView;
2095 }
2096 if (child.getVisibility() != View.GONE) {
2097 previousView = (ExpandableView) child;
2098 }
2099 }
2100 return null;
2101 }
2102
2103 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002104 * @return The first child which has visibility unequal to GONE which is currently below the
2105 * given translationY or equal to it.
2106 */
Selim Cinekef8c2252017-02-10 14:52:18 -08002107 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002108 int childCount = getChildCount();
2109 for (int i = 0; i < childCount; i++) {
2110 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002111 if (child.getVisibility() == View.GONE) {
2112 continue;
2113 }
2114 float rowTranslation = child.getTranslationY();
2115 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002116 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002117 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2118 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2119 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2120 List<ExpandableNotificationRow> notificationChildren =
2121 row.getNotificationChildren();
2122 for (int childIndex = 0; childIndex < notificationChildren.size();
2123 childIndex++) {
2124 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2125 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2126 return rowChild;
2127 }
2128 }
2129 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002130 }
2131 }
2132 return null;
2133 }
2134
2135 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002136 * @return the last child which has visibility unequal to GONE
2137 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002138 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002139 int childCount = getChildCount();
2140 for (int i = childCount - 1; i >= 0; i--) {
2141 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002142 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002143 return child;
2144 }
2145 }
2146 return null;
2147 }
2148
Jorim Jaggi069cd032014-05-15 03:09:01 +02002149 /**
2150 * @return the number of children which have visibility unequal to GONE
2151 */
2152 public int getNotGoneChildCount() {
2153 int childCount = getChildCount();
2154 int count = 0;
2155 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002156 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002157 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002158 count++;
2159 }
2160 }
2161 return count;
2162 }
2163
Selim Cinek67b22602014-03-10 15:40:16 +01002164 private void updateContentHeight() {
2165 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002166 float previousPaddingRequest = mPaddingBetweenElements;
2167 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002168 int numShownItems = 0;
2169 boolean finish = false;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002170 int maxDisplayedNotifications = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002171 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08002172 : mMaxDisplayedNotifications;
2173
Selim Cinek67b22602014-03-10 15:40:16 +01002174 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002175 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002176 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002177 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002178 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002179 boolean limitReached = maxDisplayedNotifications != -1
2180 && numShownItems >= maxDisplayedNotifications;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002181 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002182 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07002183 && expandableView instanceof ExpandableNotificationRow
2184 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
2185 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002186 expandableView = mShelf;
2187 finish = true;
2188 }
Selim Cinek42357e02016-02-24 18:48:01 -08002189 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002190 float padding;
2191 if (increasedPaddingAmount >= 0.0f) {
2192 padding = (int) NotificationUtils.interpolate(
2193 previousPaddingRequest,
2194 mIncreasedPaddingBetweenElements,
2195 increasedPaddingAmount);
2196 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002197 mPaddingBetweenElements,
2198 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002199 increasedPaddingAmount);
2200 } else {
2201 int ownPadding = (int) NotificationUtils.interpolate(
2202 0,
2203 mPaddingBetweenElements,
2204 1.0f + increasedPaddingAmount);
2205 if (previousPaddingAmount > 0.0f) {
2206 padding = (int) NotificationUtils.interpolate(
2207 ownPadding,
2208 mIncreasedPaddingBetweenElements,
2209 previousPaddingAmount);
2210 } else {
2211 padding = ownPadding;
2212 }
2213 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002214 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002215 if (height != 0) {
2216 height += padding;
2217 }
2218 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002219 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002220 numShownItems++;
2221 if (finish) {
2222 break;
2223 }
Selim Cinek67b22602014-03-10 15:40:16 +01002224 }
2225 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002226 mIntrinsicContentHeight = height;
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002227
2228 // We don't want to use the toppadding since that might be interpolated and we want
2229 // to take the final value of the animation.
2230 int topPadding = mAmbientState.isFullyDark() ? mDarkTopPadding : mRegularTopPadding;
2231 mContentHeight = height + topPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002232 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002233 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002234 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002235 }
2236
Adrian Roos7d062c42017-03-30 15:11:43 -07002237 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002238 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002239 }
2240
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002241 @Override
Selim Cinekbe2c4432017-05-30 12:11:09 -07002242 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002243 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002244 }
2245
Selim Cinekc22fff62016-05-20 12:44:30 -07002246 private void updateScrollability() {
Riddle Hsu065c01c2018-05-10 23:14:19 +08002247 boolean scrollable = !mQsExpanded && getScrollRange() > 0;
Selim Cinekc22fff62016-05-20 12:44:30 -07002248 if (scrollable != mScrollable) {
2249 mScrollable = scrollable;
2250 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002251 updateForwardAndBackwardScrollability();
2252 }
2253 }
2254
2255 private void updateForwardAndBackwardScrollability() {
2256 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2257 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2258 boolean changed = forwardScrollable != mForwardScrollable
2259 || backwardsScrollable != mBackwardScrollable;
2260 mForwardScrollable = forwardScrollable;
2261 mBackwardScrollable = backwardsScrollable;
2262 if (changed) {
2263 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002264 }
Selim Cinek67b22602014-03-10 15:40:16 +01002265 }
2266
Selim Cinek6811d722016-01-19 17:53:12 -08002267 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002268 // No need to update the background color if it's not being drawn.
Lucas Dupin16cfe452018-02-08 13:14:50 -08002269 if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002270 return;
2271 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002272
Selim Cinek6811d722016-01-19 17:53:12 -08002273 updateBackgroundBounds();
2274 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002275 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2276 || areBoundsAnimating();
2277 if (!isExpanded()) {
2278 abortBackgroundAnimators();
2279 animate = false;
2280 }
2281 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002282 startBackgroundAnimation();
2283 } else {
2284 mCurrentBounds.set(mBackgroundBounds);
2285 applyCurrentBackgroundBounds();
2286 }
2287 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002288 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002289 }
Selim Cinek614576e2016-01-20 10:54:09 -08002290 mAnimateNextBackgroundBottom = false;
2291 mAnimateNextBackgroundTop = false;
2292 }
2293
Selim Cinek54680902016-10-19 16:49:44 -07002294 private void abortBackgroundAnimators() {
2295 if (mBottomAnimator != null) {
2296 mBottomAnimator.cancel();
2297 }
2298 if (mTopAnimator != null) {
2299 mTopAnimator.cancel();
2300 }
2301 }
2302
Selim Cinek614576e2016-01-20 10:54:09 -08002303 private boolean areBoundsAnimating() {
2304 return mBottomAnimator != null || mTopAnimator != null;
2305 }
2306
2307 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002308 // left and right are always instantly applied
2309 mCurrentBounds.left = mBackgroundBounds.left;
2310 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002311 startBottomAnimation();
2312 startTopAnimation();
2313 }
2314
2315 private void startTopAnimation() {
2316 int previousEndValue = mEndAnimationRect.top;
2317 int newEndValue = mBackgroundBounds.top;
2318 ObjectAnimator previousAnimator = mTopAnimator;
2319 if (previousAnimator != null && previousEndValue == newEndValue) {
2320 return;
2321 }
2322 if (!mAnimateNextBackgroundTop) {
2323 // just a local update was performed
2324 if (previousAnimator != null) {
2325 // we need to increase all animation keyframes of the previous animator by the
2326 // relative change to the end value
2327 int previousStartValue = mStartAnimationRect.top;
2328 PropertyValuesHolder[] values = previousAnimator.getValues();
2329 values[0].setIntValues(previousStartValue, newEndValue);
2330 mStartAnimationRect.top = previousStartValue;
2331 mEndAnimationRect.top = newEndValue;
2332 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2333 return;
2334 } else {
2335 // no new animation needed, let's just apply the value
2336 setBackgroundTop(newEndValue);
2337 return;
2338 }
2339 }
2340 if (previousAnimator != null) {
2341 previousAnimator.cancel();
2342 }
2343 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2344 mCurrentBounds.top, newEndValue);
2345 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2346 animator.setInterpolator(interpolator);
2347 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2348 // remove the tag when the animation is finished
2349 animator.addListener(new AnimatorListenerAdapter() {
2350 @Override
2351 public void onAnimationEnd(Animator animation) {
2352 mStartAnimationRect.top = -1;
2353 mEndAnimationRect.top = -1;
2354 mTopAnimator = null;
2355 }
2356 });
2357 animator.start();
2358 mStartAnimationRect.top = mCurrentBounds.top;
2359 mEndAnimationRect.top = newEndValue;
2360 mTopAnimator = animator;
2361 }
2362
2363 private void startBottomAnimation() {
2364 int previousStartValue = mStartAnimationRect.bottom;
2365 int previousEndValue = mEndAnimationRect.bottom;
2366 int newEndValue = mBackgroundBounds.bottom;
2367 ObjectAnimator previousAnimator = mBottomAnimator;
2368 if (previousAnimator != null && previousEndValue == newEndValue) {
2369 return;
2370 }
2371 if (!mAnimateNextBackgroundBottom) {
2372 // just a local update was performed
2373 if (previousAnimator != null) {
2374 // we need to increase all animation keyframes of the previous animator by the
2375 // relative change to the end value
2376 PropertyValuesHolder[] values = previousAnimator.getValues();
2377 values[0].setIntValues(previousStartValue, newEndValue);
2378 mStartAnimationRect.bottom = previousStartValue;
2379 mEndAnimationRect.bottom = newEndValue;
2380 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2381 return;
2382 } else {
2383 // no new animation needed, let's just apply the value
2384 setBackgroundBottom(newEndValue);
2385 return;
2386 }
2387 }
2388 if (previousAnimator != null) {
2389 previousAnimator.cancel();
2390 }
2391 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2392 mCurrentBounds.bottom, newEndValue);
2393 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2394 animator.setInterpolator(interpolator);
2395 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2396 // remove the tag when the animation is finished
2397 animator.addListener(new AnimatorListenerAdapter() {
2398 @Override
2399 public void onAnimationEnd(Animator animation) {
2400 mStartAnimationRect.bottom = -1;
2401 mEndAnimationRect.bottom = -1;
2402 mBottomAnimator = null;
2403 }
2404 });
2405 animator.start();
2406 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2407 mEndAnimationRect.bottom = newEndValue;
2408 mBottomAnimator = animator;
2409 }
2410
2411 private void setBackgroundTop(int top) {
2412 mCurrentBounds.top = top;
2413 applyCurrentBackgroundBounds();
2414 }
2415
2416 public void setBackgroundBottom(int bottom) {
2417 mCurrentBounds.bottom = bottom;
2418 applyCurrentBackgroundBounds();
2419 }
2420
2421 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002422 // If the background of the notification is not being drawn, then there is no need to
2423 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2424 if (!mShouldDrawNotificationBackground) {
2425 return;
2426 }
2427
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002428 final boolean awake = mDarkAmount != 0 || mAmbientState.isDark();
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002429 mScrimController.setExcludedBackgroundArea(
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002430 mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002431 : mCurrentBounds);
Selim Cinek614576e2016-01-20 10:54:09 -08002432 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002433 }
2434
2435 /**
2436 * Update the background bounds to the new desired bounds
2437 */
2438 private void updateBackgroundBounds() {
shawnlin3a2a2e22018-05-04 17:09:50 +08002439 getLocationInWindow(mTempInt2);
2440 mBackgroundBounds.left = mTempInt2[0] + mSidePaddings;
2441 mBackgroundBounds.right = mTempInt2[0] + getWidth() - mSidePaddings;
2442
Selim Cinek614576e2016-01-20 10:54:09 -08002443 if (!mIsExpanded) {
2444 mBackgroundBounds.top = 0;
2445 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002446 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002447 }
2448 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002449 int top = 0;
2450 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002451 // Round Y up to avoid seeing the background during animation
2452 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002453 if (mAnimateNextBackgroundTop
2454 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2455 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002456 // we're ending up at the same location as we are now, lets just skip the animation
2457 top = finalTranslationY;
2458 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002459 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002460 }
2461 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002462 ActivatableNotificationView lastView =
2463 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2464 ? mShelf
2465 : mLastVisibleBackgroundChild;
2466 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002467 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002468 int finalTranslationY;
2469 if (lastView == mShelf) {
2470 finalTranslationY = (int) mShelf.getTranslationY();
2471 } else {
2472 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2473 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002474 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002475 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek614576e2016-01-20 10:54:09 -08002476 if (mAnimateNextBackgroundBottom
2477 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2478 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002479 // we're ending up at the same location as we are now, lets just skip the animation
2480 bottom = finalBottom;
2481 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002482 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2483 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002484 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002485 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002486 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002487 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002488 }
Selim Cinek355652a2016-12-07 13:32:12 -08002489 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002490 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002491 } else {
2492 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002493 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002494 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002495 mBackgroundBounds.top = top;
shawnlinc3457912018-05-15 16:39:56 +08002496 mBackgroundBounds.bottom = Math.max(bottom, top);
Selim Cinek6811d722016-01-19 17:53:12 -08002497 }
2498
Selim Cinek614576e2016-01-20 10:54:09 -08002499 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2500 int childCount = getChildCount();
2501 for (int i = 0; i < childCount; i++) {
2502 View child = getChildAt(i);
2503 if (child.getVisibility() != View.GONE
2504 && child instanceof ExpandableNotificationRow) {
2505 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2506 if (row.isPinned()) {
2507 return row;
2508 }
2509 }
2510 }
2511 return null;
2512 }
2513
2514 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002515 int childCount = getChildCount();
2516 for (int i = childCount - 1; i >= 0; i--) {
2517 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002518 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2519 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002520 return (ActivatableNotificationView) child;
2521 }
2522 }
2523 return null;
2524 }
2525
Selim Cinek614576e2016-01-20 10:54:09 -08002526 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002527 int childCount = getChildCount();
2528 for (int i = 0; i < childCount; i++) {
2529 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002530 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2531 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002532 return (ActivatableNotificationView) child;
2533 }
2534 }
2535 return null;
2536 }
2537
Selim Cinek67b22602014-03-10 15:40:16 +01002538 /**
2539 * Fling the scroll view
2540 *
2541 * @param velocityY The initial velocity in the Y direction. Positive
2542 * numbers mean that the finger/cursor is moving down the screen,
2543 * which means we want to scroll towards the top.
2544 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002545 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002546 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002547 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002548
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002549 float topAmount = getCurrentOverScrollAmount(true);
2550 float bottomAmount = getCurrentOverScrollAmount(false);
2551 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002552 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002553 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002554 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002555 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002556 * mOverflingDistance + topAmount;
2557 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002558 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002559 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002560 mMaxOverScroll = Math.abs(velocityY) / 1000f
2561 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2562 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002563 } else {
2564 // it will be set once we reach the boundary
2565 mMaxOverScroll = 0.0f;
2566 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002567 int minScrollY = Math.max(0, scrollRange);
2568 if (mExpandedInThisMotion) {
2569 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2570 }
Selim Cinek9212de82017-02-06 16:04:28 -08002571 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2572 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002573
Selim Cinek9212de82017-02-06 16:04:28 -08002574 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002575 }
2576 }
2577
Selim Cinek1408eb52014-06-02 14:45:38 +02002578 /**
2579 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2580 * overScroll view (i.e QS).
2581 */
2582 private boolean shouldOverScrollFling(int initialVelocity) {
2583 float topOverScroll = getCurrentOverScrollAmount(true);
2584 return mScrolledToTopOnFirstDown
2585 && !mExpandedInThisMotion
2586 && topOverScroll > mMinTopOverScrollToEscape
2587 && initialVelocity > 0;
2588 }
2589
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002590 /**
2591 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2592 * account.
2593 *
2594 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002595 * @param animate whether to animate the change
2596 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2597 * {@code qsHeight} is the final top padding
2598 */
Jason Monk16ac3772016-02-10 15:39:21 -05002599 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002600 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002601 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002602 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002603 if (topPadding + minStackHeight > getHeight()) {
2604 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002605 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002606 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002607 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002608 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002609 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002610 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002611 }
2612
shawnlin8e4e92c2018-04-12 18:47:24 +08002613 public void setMaxTopPadding(int maxTopPadding) {
2614 mMaxTopPadding = maxTopPadding;
2615 }
2616
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002617 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002618 if (isHeadsUpTransition()) {
2619 return getTopHeadsUpPinnedHeight();
2620 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002621 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002622 }
2623
Julia Reynolds34f14962018-05-03 12:40:20 +00002624 public int getFirstChildIntrinsicHeight() {
2625 final ExpandableView firstChild = getFirstChildNotGone();
2626 int firstChildMinHeight = firstChild != null
2627 ? firstChild.getIntrinsicHeight()
2628 : mEmptyShadeView != null
2629 ? mEmptyShadeView.getIntrinsicHeight()
2630 : mCollapsedSize;
2631 if (mOwnScrollY > 0) {
2632 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2633 }
2634 return firstChildMinHeight;
2635 }
2636
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002637 public float getTopPaddingOverflow() {
2638 return mTopPaddingOverflow;
2639 }
2640
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002641 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002642 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002643 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002644 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002645 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002646 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002647 shelfHeight = mShelf.getIntrinsicHeight();
2648 }
2649 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002650 }
2651
Selim Cinek1408eb52014-06-02 14:45:38 +02002652 private int clampPadding(int desiredPadding) {
2653 return Math.max(desiredPadding, mIntrinsicPadding);
2654 }
2655
Selim Cinekfed1ab62014-06-17 14:10:33 -07002656 private float getRubberBandFactor(boolean onTop) {
2657 if (!onTop) {
2658 return RUBBER_BAND_FACTOR_NORMAL;
2659 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002660 if (mExpandedInThisMotion) {
2661 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002662 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002663 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2664 } else if (mScrolledToTopOnFirstDown) {
2665 return 1.0f;
2666 }
2667 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002668 }
2669
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002670 /**
2671 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2672 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2673 * overscroll view (e.g. expand QS).
2674 */
2675 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002676 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002677 || !mScrolledToTopOnFirstDown;
2678 }
2679
Selim Cinek67b22602014-03-10 15:40:16 +01002680 private void endDrag() {
2681 setIsBeingDragged(false);
2682
2683 recycleVelocityTracker();
2684
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002685 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2686 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2687 }
2688 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2689 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2690 }
Selim Cinek67b22602014-03-10 15:40:16 +01002691 }
2692
Jorim Jaggi56306252014-07-03 00:40:09 +02002693 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2694 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2695 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2696 }
2697
Selim Cinek67b22602014-03-10 15:40:16 +01002698 @Override
2699 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002700 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002701 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002702 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002703 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002704 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2705 }
Selim Cinek67b22602014-03-10 15:40:16 +01002706 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002707 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002708 scrollWantsIt = onInterceptTouchEventScroll(ev);
2709 }
2710 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002711 if (!mIsBeingDragged
2712 && !mExpandingNotification
2713 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002714 && !mOnlyScrollingInThisMotion
2715 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002716 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2717 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002718 // Check if we need to clear any snooze leavebehinds
2719 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002720 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07002721 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2722 && !scrollWantsIt) {
2723 mCheckForLeavebehind = false;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002724 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
2725 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2726 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07002727 }
2728 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2729 mCheckForLeavebehind = true;
2730 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002731 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2732 }
2733
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002734 private void handleEmptySpaceClick(MotionEvent ev) {
2735 switch (ev.getActionMasked()) {
2736 case MotionEvent.ACTION_MOVE:
2737 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2738 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2739 mTouchIsClick = false;
2740 }
2741 break;
2742 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002743 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002744 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2745 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2746 }
2747 break;
2748 }
2749 }
2750
Selim Cinek1408eb52014-06-02 14:45:38 +02002751 private void initDownStates(MotionEvent ev) {
2752 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2753 mExpandedInThisMotion = false;
2754 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002755 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002756 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002757 mTouchIsClick = true;
2758 mInitialTouchX = ev.getX();
2759 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002760 }
Selim Cinek67b22602014-03-10 15:40:16 +01002761 }
2762
Selim Cinekef5127e2015-12-21 16:55:58 -08002763 public void setChildTransferInProgress(boolean childTransferInProgress) {
2764 mChildTransferInProgress = childTransferInProgress;
2765 }
2766
Christoph Studer068f5922014-04-08 17:43:07 -04002767 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002768 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002769 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002770 // we only call our internal methods if this is actually a removal and not just a
2771 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002772 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002773 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002774 }
2775 }
2776
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002777 @Override
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002778 public void cleanUpViewState(View child) {
2779 if (child == mTranslatingParentView) {
2780 mTranslatingParentView = null;
2781 }
2782 mCurrentStackScrollState.removeViewStateForView(child);
2783 }
2784
Selim Cinek197823d2016-03-24 13:06:00 -07002785 @Override
2786 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2787 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2788 if (disallowIntercept) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002789 cancelLongPress();
Selim Cinek197823d2016-03-24 13:06:00 -07002790 }
2791 }
2792
Selim Cineka5703182016-05-11 21:23:16 -04002793 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002794 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002795 // This is only a position change, don't do anything special
2796 return;
2797 }
Selim Cinekd1395642016-04-28 12:22:42 -07002798 ExpandableView expandableView = (ExpandableView) child;
2799 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002800 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002801 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002802 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002803 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002804 if (!mSwipedOutViews.contains(child)
2805 || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002806 container.addTransientView(child, 0);
2807 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002808 }
2809 } else {
2810 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002811 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002812 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002813
Selim Cineke9bad242016-06-15 11:46:37 -07002814 focusNextViewIfFocused(child);
2815 }
2816
2817 private void focusNextViewIfFocused(View view) {
2818 if (view instanceof ExpandableNotificationRow) {
2819 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2820 if (row.shouldRefocusOnDismiss()) {
2821 View nextView = row.getChildAfterViewWhenDismissed();
2822 if (nextView == null) {
2823 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2824 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2825 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002826 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002827 }
2828 if (nextView != null) {
2829 nextView.requestAccessibilityFocus();
2830 }
2831 }
2832 }
2833
Selim Cinekc27437b2014-05-14 10:23:33 +02002834 }
2835
Selim Cinekb5605e52015-02-20 18:21:41 +01002836 private boolean isChildInGroup(View child) {
2837 return child instanceof ExpandableNotificationRow
2838 && mGroupManager.isChildInGroupWithSummary(
2839 ((ExpandableNotificationRow) child).getStatusBarNotification());
2840 }
2841
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002842 /**
2843 * Generate a remove animation for a child view.
2844 *
2845 * @param child The view to generate the remove animation for.
2846 * @return Whether an animation was generated.
2847 */
2848 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002849 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002850 mAddedHeadsUpChildren.remove(child);
2851 return false;
2852 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002853 if (isClickedHeadsUp(child)) {
Selim Cinek9dd0d042018-05-14 18:12:42 -07002854 // An animation is already running, add it transiently
2855 mClearTransientViewsWhenFinished.add((ExpandableView) child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002856 return true;
2857 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002858 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002859 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002860 // Generate Animations
2861 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002862 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002863 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002864 } else {
2865 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002866 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002867 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002868 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002869 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002870 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002871 }
2872
Selim Cinek0fccc722015-07-29 17:04:36 -07002873 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002874 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002875 }
2876
Selim Cineke0890e52015-06-17 11:17:08 -07002877 /**
2878 * Remove a removed child view from the heads up animations if it was just added there
2879 *
2880 * @return whether any child was removed from the list to animate
2881 */
2882 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2883 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002884 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2885 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002886 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002887 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002888 mTmpList.add(eventPair);
2889 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002890 }
2891 }
Selim Cineke0890e52015-06-17 11:17:08 -07002892 if (hasAddEvent) {
2893 // This child was just added lets remove all events.
2894 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002895 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002896 }
2897 mTmpList.clear();
2898 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002899 }
2900
Selim Cinek572bbd42014-04-25 16:43:27 +02002901 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002902 * @param child the child to query
2903 * @return whether a view is not a top level child but a child notification and that group is
2904 * not expanded
2905 */
2906 private boolean isChildInInvisibleGroup(View child) {
2907 if (child instanceof ExpandableNotificationRow) {
2908 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2909 ExpandableNotificationRow groupSummary =
2910 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2911 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002912 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002913 }
2914 }
2915 return false;
2916 }
2917
2918 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002919 * Updates the scroll position when a child was removed
2920 *
2921 * @param removedChild the removed child
2922 */
Selim Cinek61633a82016-01-25 15:54:10 -08002923 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002924 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002925 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2926 int padding;
2927 if (increasedPaddingAmount >= 0) {
2928 padding = (int) NotificationUtils.interpolate(
2929 mPaddingBetweenElements,
2930 mIncreasedPaddingBetweenElements,
2931 increasedPaddingAmount);
2932 } else {
2933 padding = (int) NotificationUtils.interpolate(
2934 0,
2935 mPaddingBetweenElements,
2936 1.0f + increasedPaddingAmount);
2937 }
Selim Cinek61633a82016-01-25 15:54:10 -08002938 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002939 int endPosition = startingPosition + childHeight;
2940 if (endPosition <= mOwnScrollY) {
2941 // This child is fully scrolled of the top, so we have to deduct its height from the
2942 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002943 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002944 } else if (startingPosition < mOwnScrollY) {
2945 // This child is currently being scrolled into, set the scroll position to the start of
2946 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002947 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002948 }
2949 }
2950
Selim Cinekd7c4e002014-07-04 18:36:42 +02002951 private int getIntrinsicHeight(View view) {
2952 if (view instanceof ExpandableView) {
2953 ExpandableView expandableView = (ExpandableView) view;
2954 return expandableView.getIntrinsicHeight();
2955 }
2956 return view.getHeight();
2957 }
2958
Selim Cinek1f624952017-06-08 19:11:50 -07002959 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002960 ExpandableNotificationRow childInGroup = null;
2961 ExpandableNotificationRow requestedRow = null;
2962 if (isChildInGroup(requestedView)) {
2963 // We're asking for a child in a group. Calculate the position of the parent first,
2964 // then within the parent.
2965 childInGroup = (ExpandableNotificationRow) requestedView;
2966 requestedView = requestedRow = childInGroup.getNotificationParent();
2967 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002968 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002969 float previousPaddingRequest = mPaddingBetweenElements;
2970 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002971 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002972 ExpandableView child = (ExpandableView) getChildAt(i);
2973 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002974 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002975 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002976 float padding;
2977 if (increasedPaddingAmount >= 0.0f) {
2978 padding = (int) NotificationUtils.interpolate(
2979 previousPaddingRequest,
2980 mIncreasedPaddingBetweenElements,
2981 increasedPaddingAmount);
2982 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002983 mPaddingBetweenElements,
2984 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002985 increasedPaddingAmount);
2986 } else {
2987 int ownPadding = (int) NotificationUtils.interpolate(
2988 0,
2989 mPaddingBetweenElements,
2990 1.0f + increasedPaddingAmount);
2991 if (previousPaddingAmount > 0.0f) {
2992 padding = (int) NotificationUtils.interpolate(
2993 ownPadding,
2994 mIncreasedPaddingBetweenElements,
2995 previousPaddingAmount);
2996 } else {
2997 padding = ownPadding;
2998 }
2999 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08003000 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08003001 if (position != 0) {
3002 position += padding;
3003 }
3004 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08003005 }
Adrian Roos4a579672016-05-24 16:54:37 -07003006 if (child == requestedView) {
3007 if (requestedRow != null) {
3008 position += requestedRow.getPositionOfChild(childInGroup);
3009 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003010 return position;
3011 }
Selim Cinek61633a82016-01-25 15:54:10 -08003012 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02003013 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003014 }
3015 }
3016 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02003017 }
3018
3019 @Override
Adam Powell6690d012015-06-17 16:41:56 -07003020 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02003021 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01003022 onViewAddedInternal(child);
3023 }
3024
Selim Cinek614576e2016-01-20 10:54:09 -08003025 private void updateFirstAndLastBackgroundViews() {
3026 ActivatableNotificationView firstChild = getFirstChildWithBackground();
3027 ActivatableNotificationView lastChild = getLastChildWithBackground();
3028 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek29aab962018-02-27 17:05:45 -08003029 mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
3030 mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
Selim Cinek614576e2016-01-20 10:54:09 -08003031 } else {
3032 mAnimateNextBackgroundTop = false;
3033 mAnimateNextBackgroundBottom = false;
3034 }
3035 mFirstVisibleBackgroundChild = firstChild;
3036 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08003037 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek29aab962018-02-27 17:05:45 -08003038 mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
3039 mLastVisibleBackgroundChild);
Selim Cinek515b2032017-11-15 10:20:19 -08003040 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003041 }
3042
Selim Cinekb5605e52015-02-20 18:21:41 +01003043 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003044 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02003045 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003046 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003047 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003048 updateChronometerForChild(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003049 }
3050
Selim Cinekd06c41c2015-07-06 14:51:36 -07003051 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04003052 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003053 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04003054 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003055 }
3056 }
3057
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003058 @Override
Selim Cinekd1395642016-04-28 12:22:42 -07003059 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
3060 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003061 }
3062
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003063 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003064 public void notifyGroupChildAdded(View row) {
3065 onViewAddedInternal(row);
3066 }
3067
Jorim Jaggi75c95042014-05-16 19:09:59 +02003068 public void setAnimationsEnabled(boolean animationsEnabled) {
3069 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003070 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003071 if (!animationsEnabled) {
3072 mSwipedOutViews.clear();
3073 mChildrenToRemoveAnimated.clear();
3074 clearTemporaryViewsInGroup(this);
3075 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003076 }
3077
3078 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003079 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003080 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003081 int childCount = getChildCount();
3082 for (int i = 0; i < childCount; i++) {
3083 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003084 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003085 updateAnimationState(running, child);
3086 }
3087 }
3088
Selim Cinek51ae05d2014-09-09 15:51:38 +02003089 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003090 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003091 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003092 }
3093
Selim Cinek2627d722018-01-19 12:16:49 -08003094 @Override
3095 public void setExpandingNotification(ExpandableNotificationRow row) {
3096 mAmbientState.setExpandingNotification(row);
3097 requestChildrenUpdate();
3098 }
3099
3100 @Override
Selim Cinek8875de12018-03-22 10:14:32 -07003101 public void bindRow(ExpandableNotificationRow row) {
Selim Cinekf0c79e12018-05-14 17:17:31 -07003102 row.setHeadsUpAnimatingAwayListener(animatingAway -> {
3103 mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
3104 mHeadsUpAppearanceController.updateHeader(row.getEntry());
3105 });
Selim Cinek8875de12018-03-22 10:14:32 -07003106 }
3107
3108 @Override
Selim Cinek2627d722018-01-19 12:16:49 -08003109 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3110 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3111 requestChildrenUpdate();
3112 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003113
Selim Cinekcab4a602014-09-03 14:47:57 +02003114 private void updateAnimationState(boolean running, View child) {
3115 if (child instanceof ExpandableNotificationRow) {
3116 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3117 row.setIconAnimationRunning(running);
3118 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003119 }
3120
3121 public boolean isAddOrRemoveAnimationPending() {
3122 return mNeedsAnimation
3123 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3124 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003125
3126 @Override
Jorim Jaggif6411742014-08-05 17:10:43 +00003127 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003128 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003129 // Generate Animations
3130 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003131 if (fromMoreCard) {
3132 mFromMoreCardAdditions.add(child);
3133 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003134 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003135 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003136 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003137 mAddedHeadsUpChildren.add(child);
3138 mChildrenToAddAnimated.remove(child);
3139 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003140 }
3141
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003142 @Override
Selim Cinek572bbd42014-04-25 16:43:27 +02003143 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003144 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003145
3146 if (currentIndex == -1) {
3147 boolean isTransient = false;
3148 if (child instanceof ExpandableNotificationRow
3149 && ((ExpandableNotificationRow)child).getTransientContainer() != null) {
3150 isTransient = true;
3151 }
3152 Log.e(TAG, "Attempting to re-position "
3153 + (isTransient ? "transient" : "")
3154 + " view {"
3155 + child
3156 + "}");
3157 return;
3158 }
3159
Dan Sandlereceda3d2014-07-21 15:35:01 -04003160 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003161 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08003162 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003163 removeView(child);
3164 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08003165 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003166 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003167 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003168 mChildrenChangingPositions.add(child);
3169 mNeedsAnimation = true;
3170 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003171 }
3172 }
3173
Selim Cinekf4c19962014-05-01 21:55:31 +02003174 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003175 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003176 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003177 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003178 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003179 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003180 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003181 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
3182 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003183 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003184 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003185 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003186 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003187 } else {
3188 applyCurrentState();
3189 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003190 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003191 }
3192
3193 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003194 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003195 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003196 generateChildAdditionEvents();
3197 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02003198 generateSnapBackEvents();
3199 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003200 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003201 generateActivateEvent();
3202 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003203 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003204 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003205 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003206 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003207 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003208 generateAnimateEverythingEvent();
Lucas Dupin4e023812018-04-02 21:19:23 -07003209 generatePulsingAnimationEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003210 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003211 }
3212
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003213 private void generateHeadsUpAnimationEvents() {
3214 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003215 ExpandableNotificationRow row = eventPair.first;
3216 boolean isHeadsUp = eventPair.second;
3217 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3218 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003219 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003220 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003221 type = row.wasJustClicked()
3222 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3223 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003224 if (row.isChildInGroup()) {
3225 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003226 row.setHeadsUpAnimatingAway(false);
Selim Cinekf93bf3e2018-05-08 14:43:21 -07003227 continue;
Selim Cinek76e813c2016-07-14 11:16:58 -07003228 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003229 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08003230 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04003231 if (viewState == null) {
3232 // A view state was never generated for this view, so we don't need to animate
3233 // this. This may happen with notification children.
3234 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003235 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003236 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3237 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3238 // Our custom add animation
3239 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3240 } else {
3241 // Normal add animation
3242 type = AnimationEvent.ANIMATION_TYPE_ADD;
3243 }
3244 onBottom = !pinnedAndClosed;
3245 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003246 }
3247 AnimationEvent event = new AnimationEvent(row, type);
3248 event.headsUpFromBottom = onBottom;
3249 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003250 }
3251 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003252 mAddedHeadsUpChildren.clear();
3253 }
3254
Selim Cinekbbcebde2016-11-09 18:28:20 -08003255 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003256 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3257 return false;
3258 }
3259 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003260 }
3261
Selim Cinekb5605e52015-02-20 18:21:41 +01003262 private void generateGroupExpansionEvent() {
3263 // Generate a group expansion/collapsing event if there is such a group at all
3264 if (mExpandedGroupView != null) {
3265 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3266 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3267 mExpandedGroupView = null;
3268 }
3269 }
3270
Selim Cineka5e211b2014-08-11 17:35:48 +02003271 private void generateViewResizeEvent() {
3272 if (mNeedViewResizeAnimation) {
3273 mAnimationEvents.add(
3274 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3275 }
3276 mNeedViewResizeAnimation = false;
3277 }
3278
Selim Cinekeb973562014-05-02 17:07:49 +02003279 private void generateSnapBackEvents() {
3280 for (View child : mSnappedBackChildren) {
3281 mAnimationEvents.add(new AnimationEvent(child,
3282 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3283 }
3284 mSnappedBackChildren.clear();
3285 }
3286
3287 private void generateDragEvents() {
3288 for (View child : mDragAnimPendingChildren) {
3289 mAnimationEvents.add(new AnimationEvent(child,
3290 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3291 }
3292 mDragAnimPendingChildren.clear();
3293 }
3294
Selim Cinek572bbd42014-04-25 16:43:27 +02003295 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003296 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003297 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003298
3299 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003300 float removedTranslation = child.getTranslationY();
3301 boolean ignoreChildren = true;
3302 if (child instanceof ExpandableNotificationRow) {
3303 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3304 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3305 removedTranslation = row.getTranslationWhenRemoved();
3306 ignoreChildren = false;
3307 }
Selim Cinek51052042017-07-04 12:07:55 +02003308 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003309 }
Selim Cinek51052042017-07-04 12:07:55 +02003310 if (!childWasSwipedOut) {
3311 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003312 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Rohan Shaha7594962018-05-22 10:59:30 -07003313
3314 if (childWasSwipedOut && child instanceof ExpandableView) {
3315 // Clean up any potential transient views if the child has already been swiped
3316 // out, as we won't be animating it further (due to its height already being
3317 // clipped to 0.
3318 ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
3319 if (transientContainer != null) {
3320 transientContainer.removeTransientView(child);
3321 }
3322 }
Selim Cinek51052042017-07-04 12:07:55 +02003323 }
3324 int animationType = childWasSwipedOut
3325 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3326 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3327 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003328 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3329 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003330 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003331 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003332 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003333 mChildrenToRemoveAnimated.clear();
3334 }
3335
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003336 private void generatePositionChangeEvents() {
3337 for (View child : mChildrenChangingPositions) {
3338 mAnimationEvents.add(new AnimationEvent(child,
3339 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3340 }
3341 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003342 if (mGenerateChildOrderChangedEvent) {
3343 mAnimationEvents.add(new AnimationEvent(null,
3344 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3345 mGenerateChildOrderChangedEvent = false;
3346 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003347 }
3348
Selim Cinek572bbd42014-04-25 16:43:27 +02003349 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003350 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003351 if (mFromMoreCardAdditions.contains(child)) {
3352 mAnimationEvents.add(new AnimationEvent(child,
3353 AnimationEvent.ANIMATION_TYPE_ADD,
3354 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3355 } else {
3356 mAnimationEvents.add(new AnimationEvent(child,
3357 AnimationEvent.ANIMATION_TYPE_ADD));
3358 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003359 }
3360 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003361 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003362 }
3363
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003364 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003365 if (mTopPaddingNeedsAnimation) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003366 AnimationEvent event;
3367 if (mAmbientState.isDark()) {
3368 event = new AnimationEvent(null /* view */,
3369 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
3370 KeyguardSliceView.DEFAULT_ANIM_DURATION);
3371 } else {
3372 event = new AnimationEvent(null /* view */,
3373 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
3374 }
3375 mAnimationEvents.add(event);
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003376 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003377 mTopPaddingNeedsAnimation = false;
3378 }
3379
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003380 private void generateActivateEvent() {
3381 if (mActivateNeedsAnimation) {
3382 mAnimationEvents.add(
3383 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3384 }
3385 mActivateNeedsAnimation = false;
3386 }
3387
Selim Cinekd9acca52014-09-01 22:33:25 +02003388 private void generateAnimateEverythingEvent() {
3389 if (mEverythingNeedsAnimation) {
3390 mAnimationEvents.add(
3391 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3392 }
3393 mEverythingNeedsAnimation = false;
3394 }
3395
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003396 private void generateDimmedEvent() {
3397 if (mDimmedNeedsAnimation) {
3398 mAnimationEvents.add(
3399 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3400 }
3401 mDimmedNeedsAnimation = false;
3402 }
3403
Jorim Jaggiae441282014-08-01 02:45:18 +02003404 private void generateHideSensitiveEvent() {
3405 if (mHideSensitiveNeedsAnimation) {
3406 mAnimationEvents.add(
3407 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3408 }
3409 mHideSensitiveNeedsAnimation = false;
3410 }
3411
John Spurlockbf370992014-06-17 13:58:31 -04003412 private void generateDarkEvent() {
3413 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003414 AnimationEvent ev = new AnimationEvent(null,
3415 AnimationEvent.ANIMATION_TYPE_DARK,
3416 new AnimationFilter()
3417 .animateDark()
3418 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003419 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3420 mAnimationEvents.add(ev);
Lucas Dupinb98ddb42018-05-29 17:26:35 -07003421 startDarkAmountAnimation();
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);
Jason Changb4e879d2018-04-11 11:17:58 +08003949 final int textColor = Utils.getColorAttrDefaultColor(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);
John Spurlockbf370992014-06-17 13:58:31 -04003997 mNeedsAnimation = true;
Selim Cinek6811d722016-01-19 17:53:12 -08003998 } else {
Lucas Dupinb98ddb42018-05-29 17:26:35 -07003999 if (mDarkAmountAnimator != null) {
4000 mDarkAmountAnimator.cancel();
4001 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004002 setDarkAmount(dark ? 1f : 0f);
Selim Cinek6811d722016-01-19 17:53:12 -08004003 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08004004 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004005 requestChildrenUpdate();
4006 applyCurrentBackgroundBounds();
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004007 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004008 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04004009 }
4010
Bill Line60aa1e2018-06-13 18:07:15 +08004011 private void updatePanelTranslation() {
4012 setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mDarkAmount);
4013 }
4014
4015 public void setVerticalPanelTranslation(float verticalPanelTranslation) {
4016 mVerticalPanelTranslation = verticalPanelTranslation;
4017 updatePanelTranslation();
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004018 }
4019
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004020 /**
4021 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
4022 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
4023 * {@link #mAmbientState}'s dark mode is toggled.
4024 */
4025 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08004026 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07004027 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004028 }
4029
Lucas Dupind285cf02018-01-18 09:18:23 -08004030 private void setDarkAmount(float darkAmount) {
4031 mDarkAmount = darkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -07004032 boolean wasFullyDark = mAmbientState.isFullyDark();
4033 mAmbientState.setDarkAmount(darkAmount);
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004034 boolean nowFullyDark = mAmbientState.isFullyDark();
4035 if (nowFullyDark != wasFullyDark) {
Lucas Dupin16cfe452018-02-08 13:14:50 -08004036 updateContentHeight();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004037 DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004038 if (nowFullyDark && dozeParameters.shouldControlScreenOff()) {
Lucas Dupinb561eda2018-04-09 17:25:04 -07004039 mShelf.fadeInTranslating();
4040 }
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004041 if (mIconAreaController != null) {
4042 mIconAreaController.setFullyDark(nowFullyDark);
4043 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08004044 }
Lucas Dupin60661a62018-04-12 10:50:13 -07004045 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07004046 updateBackgroundDimming();
Bill Line60aa1e2018-06-13 18:07:15 +08004047 updatePanelTranslation();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004048 requestChildrenUpdate();
Selim Cinek972123d2016-05-03 14:25:58 -07004049 }
4050
Lucas Dupind285cf02018-01-18 09:18:23 -08004051 public float getDarkAmount() {
4052 return mDarkAmount;
Selim Cinek972123d2016-05-03 14:25:58 -07004053 }
4054
Lucas Dupin439bd442018-06-12 15:05:28 -07004055 /**
4056 * Cancel any previous dark animations - to avoid race conditions - and creates a new one.
4057 * This function also sets {@code mBackgroundXFactor} based on the current {@code mDarkAmount}.
4058 */
Lucas Dupinb98ddb42018-05-29 17:26:35 -07004059 private void startDarkAmountAnimation() {
Lucas Dupin439bd442018-06-12 15:05:28 -07004060 boolean dark = mAmbientState.isDark();
4061 if (mDarkAmountAnimator != null) {
4062 mDarkAmountAnimator.cancel();
4063 }
4064
4065 long duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
4066 // Longer animation when sleeping with more than 1 notification
4067 if (dark && getNotGoneChildCount() > 2) {
4068 duration *= 1.2f;
4069 }
4070
4071 mDarkAmountAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount,
4072 dark ? 1f : 0);
4073 // We only swap the scaling factor if we're fully dark or fully awake to avoid
4074 // interpolation issues when playing with the power button.
4075 if (mDarkAmount == 0 || mDarkAmount == 1) {
4076 mBackgroundXFactor = dark ? 2.5f : 1.5f;
4077 }
4078 mDarkAmountAnimator.setDuration(duration);
4079 mDarkAmountAnimator.setInterpolator(Interpolators.LINEAR);
4080 mDarkAmountAnimator.addListener(new AnimatorListenerAdapter() {
Lucas Dupinb98ddb42018-05-29 17:26:35 -07004081 @Override
4082 public void onAnimationEnd(Animator animation) {
4083 mDarkAmountAnimator = null;
4084 }
4085 });
Lucas Dupinb98ddb42018-05-29 17:26:35 -07004086 mDarkAmountAnimator.start();
Selim Cinek972123d2016-05-03 14:25:58 -07004087 }
4088
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004089 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07004090 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004091 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4092 }
4093 if (screenLocation.y > getBottomMostNotificationBottom()) {
4094 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
4095 }
4096 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
4097 if (child != null) {
4098 return getNotGoneIndex(child);
4099 } else {
4100 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4101 }
4102 }
4103
4104 private int getNotGoneIndex(View child) {
4105 int count = getChildCount();
4106 int notGoneIndex = 0;
4107 for (int i = 0; i < count; i++) {
4108 View v = getChildAt(i);
4109 if (child == v) {
4110 return notGoneIndex;
4111 }
4112 if (v.getVisibility() != View.GONE) {
4113 notGoneIndex++;
4114 }
4115 }
4116 return -1;
4117 }
4118
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004119 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004120 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004121 if (mFooterView != null) {
4122 index = indexOfChild(mFooterView);
4123 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004124 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004125 mFooterView = footerView;
4126 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004127 }
4128
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004129 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004130 int index = -1;
4131 if (mEmptyShadeView != null) {
4132 index = indexOfChild(mEmptyShadeView);
4133 removeView(mEmptyShadeView);
4134 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004135 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004136 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004137 }
4138
4139 public void updateEmptyShadeView(boolean visible) {
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004140 mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
Lucas Dupinc9274ff2018-05-09 17:40:20 -07004141
4142 int oldTextRes = mEmptyShadeView.getTextResource();
4143 int newTextRes = mStatusBar.areNotificationsHidden()
4144 ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
4145 if (oldTextRes != newTextRes) {
4146 mEmptyShadeView.setText(newTextRes);
4147 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004148 }
4149
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004150 public void updateFooterView(boolean visible, boolean showDismissView) {
4151 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004152 return;
4153 }
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004154 boolean animate = mIsExpanded && mAnimationsEnabled;
4155 mFooterView.setVisible(visible, animate);
4156 mFooterView.setSecondaryVisible(showDismissView, animate);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004157 }
4158
4159 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4160 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004161 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004162 handleDismissAllClipping();
4163 }
4164
4165 private void handleDismissAllClipping() {
4166 final int count = getChildCount();
4167 boolean previousChildWillBeDismissed = false;
4168 for (int i = 0; i < count; i++) {
4169 ExpandableView child = (ExpandableView) getChildAt(i);
4170 if (child.getVisibility() == GONE) {
4171 continue;
4172 }
4173 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4174 child.setMinClipTopAmount(child.getClipTopAmount());
4175 } else {
4176 child.setMinClipTopAmount(0);
4177 }
4178 previousChildWillBeDismissed = canChildBeDismissed(child);
4179 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004180 }
4181
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004182 public boolean isFooterViewNotGone() {
4183 return mFooterView != null
4184 && mFooterView.getVisibility() != View.GONE
4185 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004186 }
4187
Selim Cinek38475442018-05-18 11:11:46 -07004188 public boolean isFooterViewContentVisible() {
4189 return mFooterView != null && mFooterView.isContentVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004190 }
4191
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004192 public int getFooterViewHeight() {
4193 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004194 }
4195
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004196 public int getEmptyShadeViewHeight() {
4197 return mEmptyShadeView.getHeight();
4198 }
4199
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004200 public float getBottomMostNotificationBottom() {
4201 final int count = getChildCount();
4202 float max = 0;
4203 for (int childIdx = 0; childIdx < count; childIdx++) {
4204 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4205 if (child.getVisibility() == GONE) {
4206 continue;
4207 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004208 float bottom = child.getTranslationY() + child.getActualHeight()
4209 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004210 if (bottom > max) {
4211 max = bottom;
4212 }
4213 }
Selim Cinekd2281152015-04-10 14:37:46 -07004214 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004215 }
4216
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004217 public void setStatusBar(StatusBar statusBar) {
4218 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004219 }
4220
Selim Cinekb5605e52015-02-20 18:21:41 +01004221 public void setGroupManager(NotificationGroupManager groupManager) {
4222 this.mGroupManager = groupManager;
4223 }
4224
Selim Cinekd9acca52014-09-01 22:33:25 +02004225 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01004226 requestAnimateEverything();
4227 }
4228
4229 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004230 if (mIsExpanded && mAnimationsEnabled) {
4231 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004232 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004233 requestChildrenUpdate();
4234 }
4235 }
4236
Selim Cinek04fb2582015-06-02 19:58:09 +02004237 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004238 int childCount = getChildCount();
4239 for (int i = childCount - 1; i >= 0; i--) {
4240 ExpandableView child = (ExpandableView) getChildAt(i);
4241 if (child.getVisibility() != View.GONE) {
4242 float childTop = child.getY();
4243 if (childTop > touchY) {
4244 // we are above a notification entirely let's abort
4245 return false;
4246 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004247 boolean belowChild = touchY > childTop + child.getActualHeight()
4248 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004249 if (child == mFooterView) {
4250 if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
Selim Cinekabf60bb2015-02-20 17:36:10 +01004251 touchY - childTop)) {
4252 // We clicked on the dismiss button
4253 return false;
4254 }
4255 } else if (child == mEmptyShadeView) {
4256 // We arrived at the empty shade view, for which we accept all clicks
4257 return true;
4258 } else if (!belowChild){
4259 // We are on a child
4260 return false;
4261 }
4262 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004263 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004264 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004265 }
4266
Selim Cinekb5605e52015-02-20 18:21:41 +01004267 @Override
4268 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004269 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4270 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004271 if (animated) {
4272 mExpandedGroupView = changedRow;
4273 mNeedsAnimation = true;
4274 }
4275 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004276 if (!mGroupExpandedForMeasure) {
4277 onHeightChanged(changedRow, false /* needsAnimation */);
4278 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004279 runAfterAnimationFinished(new Runnable() {
4280 @Override
4281 public void run() {
4282 changedRow.onFinishedExpansionChange();
4283 }
4284 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004285 }
4286
4287 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004288 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004289 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004290 }
4291
Selim Cinekc22fff62016-05-20 12:44:30 -07004292 /** @hide */
4293 @Override
4294 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4295 super.onInitializeAccessibilityEventInternal(event);
4296 event.setScrollable(mScrollable);
4297 event.setScrollX(mScrollX);
4298 event.setScrollY(mOwnScrollY);
4299 event.setMaxScrollX(mScrollX);
4300 event.setMaxScrollY(getScrollRange());
4301 }
4302
4303 @Override
4304 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4305 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004306 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004307 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004308 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004309 info.addAction(
4310 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4311 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4312 }
Selim Cinekef406062016-09-29 17:33:13 -07004313 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004314 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4315 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4316 }
4317 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004318 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4319 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004320 }
4321
4322 /** @hide */
4323 @Override
4324 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4325 if (super.performAccessibilityActionInternal(action, arguments)) {
4326 return true;
4327 }
4328 if (!isEnabled()) {
4329 return false;
4330 }
4331 int direction = -1;
4332 switch (action) {
4333 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4334 // fall through
4335 case android.R.id.accessibilityActionScrollDown:
4336 direction = 1;
4337 // fall through
4338 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4339 // fall through
4340 case android.R.id.accessibilityActionScrollUp:
4341 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004342 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004343 final int targetScrollY = Math.max(0,
4344 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4345 if (targetScrollY != mOwnScrollY) {
4346 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004347 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004348 return true;
4349 }
4350 break;
4351 }
4352 return false;
4353 }
4354
Selim Cinekef5127e2015-12-21 16:55:58 -08004355 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004356 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004357 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004358 }
4359
4360 public void generateChildOrderChangedEvent() {
4361 if (mIsExpanded && mAnimationsEnabled) {
4362 mGenerateChildOrderChangedEvent = true;
4363 mNeedsAnimation = true;
4364 requestChildrenUpdate();
4365 }
4366 }
4367
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004368 @Override
4369 public int getContainerChildCount() {
4370 return getChildCount();
4371 }
4372
4373 @Override
4374 public View getContainerChildAt(int i) {
4375 return getChildAt(i);
4376 }
4377
4378 @Override
4379 public void removeContainerView(View v) {
4380 removeView(v);
4381 }
4382
4383 @Override
4384 public void addContainerView(View v) {
4385 addView(v);
4386 }
4387
Selim Cinek684a4422015-04-15 16:18:39 -07004388 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004389 mAnimationFinishedRunnables.add(runnable);
4390 }
4391
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004392 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004393 mHeadsUpManager = headsUpManager;
4394 mAmbientState.setHeadsUpManager(headsUpManager);
Selim Cinek29aab962018-02-27 17:05:45 -08004395 mHeadsUpManager.addListener(mRoundnessManager);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004396 }
4397
4398 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004399 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004400 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4401 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004402 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004403 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004404 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004405 requestChildrenUpdate();
4406 }
4407 }
4408
4409 public void setShadeExpanded(boolean shadeExpanded) {
4410 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004411 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004412 }
4413
Selim Cineka59ecc32015-04-07 10:51:49 -07004414 /**
4415 * Set the boundary for the bottom heads up position. The heads up will always be above this
4416 * position.
4417 *
4418 * @param height the height of the screen
4419 * @param bottomBarHeight the height of the bar on the bottom
4420 */
4421 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4422 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4423 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004424 requestChildrenUpdate();
4425 }
4426
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004427 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
4428 mTrackingHeadsUp = row != null;
4429 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004430 }
4431
Selim Cinekaac93252015-04-14 20:04:12 -07004432 public void setScrimController(ScrimController scrimController) {
4433 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004434 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004435 }
4436
Selim Cinekbbc580b2015-06-03 14:11:03 +02004437 public void forceNoOverlappingRendering(boolean force) {
4438 mForceNoOverlappingRendering = force;
4439 }
4440
4441 @Override
4442 public boolean hasOverlappingRendering() {
4443 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4444 }
4445
Selim Cinek6811d722016-01-19 17:53:12 -08004446 public void setAnimationRunning(boolean animationRunning) {
4447 if (animationRunning != mAnimationRunning) {
4448 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004449 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004450 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004451 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004452 }
4453 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004454 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004455 }
4456 }
4457
Selim Cinek3776fe02016-02-04 13:32:43 -08004458 public boolean isExpanded() {
4459 return mIsExpanded;
4460 }
4461
Lucas Dupin4e023812018-04-02 21:19:23 -07004462 public void setPulsing(boolean pulsing, boolean animated) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004463 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004464 return;
4465 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004466 mPulsing = pulsing;
Lucas Dupin4e023812018-04-02 21:19:23 -07004467 mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
Selim Cinekebf42342017-07-13 15:46:10 +02004468 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004469 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004470 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004471 updateContentHeight();
Adrian Roosd83e9992017-03-16 15:17:57 -07004472 requestChildrenUpdate();
Lucas Dupin4e023812018-04-02 21:19:23 -07004473 notifyHeightChangeListener(null, animated);
4474 mNeedsAnimation |= animated;
4475 }
4476
4477 private void generatePulsingAnimationEvent() {
4478 if (mNeedingPulseAnimation != null) {
4479 int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
4480 : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
4481 mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
4482 mNeedingPulseAnimation = null;
4483 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004484 }
4485
Selim Cinek07304f5222016-05-19 18:31:36 -07004486 public void setFadingOut(boolean fadingOut) {
4487 if (fadingOut != mFadingOut) {
4488 mFadingOut = fadingOut;
4489 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004490 }
4491 }
4492
Selim Cinek48ff9b42016-11-09 19:31:51 -08004493 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4494 if (mScrimController == null) {
4495 // we're not set up yet.
4496 return;
4497 }
4498 if (parentNotFullyVisible != mParentNotFullyVisible) {
4499 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004500 updateFadingState();
4501 }
4502 }
4503
4504 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004505 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004506 updateSrcDrawing();
4507 }
4508
Selim Cinek31d37b92016-04-26 09:56:42 -07004509 @Override
4510 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4511 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004512 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004513 }
4514
Selim Cinekbc243a92016-09-27 16:35:13 -07004515 public void setQsExpanded(boolean qsExpanded) {
4516 mQsExpanded = qsExpanded;
4517 updateAlgorithmLayoutMinHeight();
Riddle Hsu065c01c2018-05-10 23:14:19 +08004518 updateScrollability();
Selim Cinekbc243a92016-09-27 16:35:13 -07004519 }
4520
shawnlin8e4e92c2018-04-12 18:47:24 +08004521 public void setQsExpansionFraction(float qsExpansionFraction) {
4522 mQsExpansionFraction = qsExpansionFraction;
4523 }
4524
Selim Cinekef406062016-09-29 17:33:13 -07004525 public void setOwnScrollY(int ownScrollY) {
4526 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004527 // We still want to call the normal scrolled changed for accessibility reasons
4528 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004529 mOwnScrollY = ownScrollY;
4530 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004531 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004532 }
4533 }
4534
Selim Cinek281c2022016-10-13 19:14:43 -07004535 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004536 int index = -1;
4537 if (mShelf != null) {
4538 index = indexOfChild(mShelf);
4539 removeView(mShelf);
4540 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004541 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004542 addView(mShelf, index);
4543 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004544 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004545 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004546 }
4547
4548 public NotificationShelf getNotificationShelf() {
4549 return mShelf;
4550 }
4551
Selim Cinekad7fac02016-10-18 17:09:15 -07004552 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4553 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4554 mMaxDisplayedNotifications = maxDisplayedNotifications;
4555 updateContentHeight();
4556 notifyHeightChangeListener(mShelf);
4557 }
4558 }
4559
shawnlin8e4e92c2018-04-12 18:47:24 +08004560 public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
4561 mShouldShowShelfOnly = shouldShowShelfOnly;
4562 updateAlgorithmLayoutMinHeight();
4563 }
4564
Selim Cinek48ff9b42016-11-09 19:31:51 -08004565 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004566 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004567 }
4568
Selim Cinekcafa87f2016-10-26 17:00:17 -07004569 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4570 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4571 updateClipping();
4572 }
4573
4574 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4575 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4576 updateClipping();
4577 }
4578
Selim Cinek355652a2016-12-07 13:32:12 -08004579 public void setStatusBarState(int statusBarState) {
4580 mStatusBarState = statusBarState;
4581 mAmbientState.setStatusBarState(statusBarState);
4582 }
4583
Selim Cinekd5ab6452016-12-08 16:34:00 -08004584 public void setExpandingVelocity(float expandingVelocity) {
4585 mAmbientState.setExpandingVelocity(expandingVelocity);
4586 }
4587
Selim Cinekfcff4c62016-12-27 14:26:06 +01004588 public float getOpeningHeight() {
4589 if (mEmptyShadeView.getVisibility() == GONE) {
4590 return getMinExpansionHeight();
4591 } else {
4592 return getAppearEndPosition();
4593 }
4594 }
4595
4596 public void setIsFullWidth(boolean isFullWidth) {
4597 mAmbientState.setPanelFullWidth(isFullWidth);
4598 }
4599
Selim Cinekec29d342017-05-05 18:31:49 -07004600 public void setUnlockHintRunning(boolean running) {
4601 mAmbientState.setUnlockHintRunning(running);
4602 }
4603
Selim Cinek5cf1d052017-06-01 17:36:46 -07004604 public void setQsCustomizerShowing(boolean isShowing) {
4605 mAmbientState.setQsCustomizerShowing(isShowing);
4606 requestChildrenUpdate();
4607 }
4608
4609 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4610 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4611 }
4612
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004613 public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
4614 mAntiBurnInOffsetX = antiBurnInOffsetX;
Bill Line60aa1e2018-06-13 18:07:15 +08004615 updatePanelTranslation();
Adrian Roosdc747bd2017-06-01 16:09:15 -07004616 }
4617
Selim Cinek707e2072017-06-30 18:32:40 +02004618 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4619 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
shawnlin8e4e92c2018-04-12 18:47:24 +08004620 + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
4621 + " qsExpandFraction=%f]",
Selim Cinek707e2072017-06-30 18:32:40 +02004622 this.getClass().getSimpleName(),
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004623 mPulsing ? "T":"f",
Selim Cinek707e2072017-06-30 18:32:40 +02004624 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4625 getVisibility() == View.VISIBLE ? "visible"
4626 : getVisibility() == View.GONE ? "gone"
4627 : "invisible",
4628 getAlpha(),
shawnlin8e4e92c2018-04-12 18:47:24 +08004629 mAmbientState.getScrollY(),
4630 mMaxTopPadding,
4631 mShouldShowShelfOnly ? "T":"f",
4632 mQsExpansionFraction));
Selim Cinek707e2072017-06-30 18:32:40 +02004633 }
4634
Lucas Dupin16cfe452018-02-08 13:14:50 -08004635 public boolean isFullyDark() {
4636 return mAmbientState.isFullyDark();
4637 }
4638
Selim Cinek7103fd42016-05-09 22:22:33 -04004639 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004640 * Add a listener whenever the expanded height changes. The first value passed as an argument
4641 * is the expanded height and the second one is the appearFraction.
4642 *
4643 * @param listener the listener to notify.
4644 */
4645 public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4646 mExpandedHeightListeners.add(listener);
4647 }
4648
4649 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07004650 * Stop a listener from listening to the expandedHeight.
4651 */
4652 public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4653 mExpandedHeightListeners.remove(listener);
4654 }
4655
Selim Cinekf0c79e12018-05-14 17:17:31 -07004656 public void setHeadsUpAppearanceController(
4657 HeadsUpAppearanceController headsUpAppearanceController) {
4658 mHeadsUpAppearanceController = headsUpAppearanceController;
4659 }
4660
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004661 public void setIconAreaController(NotificationIconAreaController controller) {
4662 mIconAreaController = controller;
4663 }
4664
Selim Cinek60ffea62018-03-22 13:16:44 -07004665 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004666 * A listener that is notified when the empty space below the notifications is clicked on
4667 */
4668 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004669 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004670 }
4671
4672 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004673 * A listener that gets notified when the overscroll at the top has changed.
4674 */
4675 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004676
4677 /**
4678 * Notifies a listener that the overscroll has changed.
4679 *
4680 * @param amount the amount of overscroll, in pixels
4681 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4682 * unrubberbanded motion to directly expand overscroll view (e.g expand
4683 * QS)
4684 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004685 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004686
4687 /**
4688 * Notify a listener that the scroller wants to escape from the scrolling motion and
4689 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4690 *
4691 * @param velocity The velocity that the Scroller had when over flinging
4692 * @param open Should the fling open or close the overscroll view.
4693 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004694 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004695 }
4696
Mady Mellor95d743c2017-01-10 12:05:27 -08004697 private class NotificationSwipeHelper extends SwipeHelper
4698 implements NotificationSwipeActionHelper {
4699 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004700 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004701 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004702
4703 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4704 super(swipeDirection, callback, context);
4705 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004706 mFalsingCheck = new Runnable() {
4707 @Override
4708 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004709 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004710 }
4711 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004712 }
4713
4714 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004715 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004716 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004717 if (mCurrMenuRow != null) {
4718 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004719 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004720 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004721 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004722
Mady Mellor95d743c2017-01-10 12:05:27 -08004723 // Slide back any notifications that might be showing a menu
4724 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004725
4726 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004727 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
Aaron Heuckroth1dd67cb2018-06-14 14:28:08 -04004728
4729 if (row.getEntry().hasFinishedInitialization()) {
4730 mCurrMenuRow = row.createMenu();
4731 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4732 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
4733 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
4734 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004735 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004736 }
4737
4738 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004739 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004740 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004741 if (mCurrMenuRow != null) {
4742 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004743 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004744 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004745
Mady Mellor95d743c2017-01-10 12:05:27 -08004746 @Override
4747 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4748 float translation) {
4749 if (mCurrMenuRow != null) {
4750 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004751 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004752 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004753 }
4754
4755 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004756 public void dismissChild(final View view, float velocity,
4757 boolean useAccelerateInterpolator) {
4758 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004759 if (mIsExpanded) {
4760 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4761 // of the panel early.
Rohan Shah524cf7b2018-03-15 14:40:02 -07004762 handleChildViewDismissed(view);
Selim Cinekd1395642016-04-28 12:22:42 -07004763 }
Eliot Courtney47098cb2017-10-18 17:30:30 +09004764 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
4765 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4766 false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004767 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004768 }
4769
4770 @Override
4771 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004772 super.snapChild(animView, targetLeft, velocity);
4773 onDragCancelled(animView);
4774 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004775 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004776 }
4777 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004778
4779 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004780 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4781 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004782 }
4783
Mady Mellorbd707492017-05-10 17:51:25 -07004784 public boolean isFalseGesture(MotionEvent ev) {
4785 return super.isFalseGesture(ev);
4786 }
4787
Mady Mellor95d743c2017-01-10 12:05:27 -08004788 private void handleMenuCoveredOrDismissed() {
4789 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4790 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004791 }
4792 }
4793
Mady Mellor4b80b102016-01-22 08:03:58 -08004794 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004795 public Animator getViewTranslationAnimator(View v, float target,
4796 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004797 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004798 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4799 } else {
4800 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004801 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004802 }
4803
4804 @Override
4805 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004806 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004807 }
4808
4809 @Override
4810 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004811 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004812 }
4813
Mady Mellor95d743c2017-01-10 12:05:27 -08004814 @Override
4815 public void dismiss(View animView, float velocity) {
4816 dismissChild(animView, velocity,
4817 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4818 }
4819
4820 @Override
4821 public void snap(View animView, float targetLeft, float velocity) {
4822 snapChild(animView, targetLeft, velocity);
4823 }
4824
4825 @Override
4826 public boolean swipedFarEnough(float translation, float viewSize) {
4827 return swipedFarEnough();
4828 }
4829
4830 @Override
4831 public boolean swipedFastEnough(float translation, float velocity) {
4832 return swipedFastEnough();
4833 }
4834
4835 @Override
4836 public float getMinDismissVelocity() {
4837 return getEscapeVelocity();
4838 }
4839
4840 public void onMenuShown(View animView) {
4841 onDragCancelled(animView);
4842
4843 // If we're on the lockscreen we want to false this.
4844 if (isAntiFalsingNeeded()) {
4845 mHandler.removeCallbacks(mFalsingCheck);
4846 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4847 }
4848 }
4849
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004850 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09004851 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004852 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004853 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4854 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004855 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004856 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004857 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004858 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004859 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004860 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004861 if (view != null && !isTouchInView(ev, view)) {
4862 // Touch was outside visible guts / menu notification, close what's visible
Eliot Courtney47098cb2017-10-18 17:30:30 +09004863 mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
4864 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4865 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004866 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004867 }
4868 }
4869
Mady Mellor95d743c2017-01-10 12:05:27 -08004870 public void resetExposedMenuView(boolean animate, boolean force) {
4871 if (mMenuExposedView == null
4872 || (!force && mMenuExposedView == mTranslatingParentView)) {
4873 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004874 return;
4875 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004876 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004877 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004878 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004879 0 /* leftTarget */, null /* updateListener */);
4880 if (anim != null) {
4881 anim.start();
4882 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004883 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
Selim Cinekb2e0f332017-08-18 12:24:38 -07004884 ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
4885 if (!row.isRemoved()) {
4886 row.resetTranslation();
4887 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004888 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004889 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004890 }
4891 }
4892
Mady Mellorc2dbe492017-03-30 13:22:03 -07004893 private boolean isTouchInView(MotionEvent ev, View view) {
4894 if (view == null) {
4895 return false;
4896 }
4897 final int height = (view instanceof ExpandableView)
4898 ? ((ExpandableView) view).getActualHeight()
4899 : view.getHeight();
4900 final int rx = (int) ev.getRawX();
4901 final int ry = (int) ev.getRawY();
4902 view.getLocationOnScreen(mTempInt2);
4903 final int x = mTempInt2[0];
4904 final int y = mTempInt2[1];
4905 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4906 boolean ret = rect.contains(rx, ry);
4907 return ret;
4908 }
4909
Selim Cinek33223572016-02-19 19:32:22 -08004910 private void updateContinuousShadowDrawing() {
4911 boolean continuousShadowUpdate = mAnimationRunning
4912 || !mAmbientState.getDraggedViews().isEmpty();
4913 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4914 if (continuousShadowUpdate) {
4915 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4916 } else {
4917 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4918 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004919 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004920 }
4921 }
4922
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004923 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004924 public void resetExposedMenuView(boolean animate, boolean force) {
4925 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004926 }
4927
4928 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4929 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4930 }
4931
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004932 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004933
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004934 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4935
4936 // ANIMATION_TYPE_ADD
4937 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004938 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004939 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004940 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004941 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004942 .animateZ()
4943 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004944
4945 // ANIMATION_TYPE_REMOVE
4946 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004947 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004948 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004949 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004950 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004951 .animateZ()
4952 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004953
4954 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4955 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004956 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004957 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004958 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004959 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004960 .animateZ()
4961 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004962
4963 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4964 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004965 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004966 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004967 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004968 .animateY()
4969 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004970 .animateZ(),
4971
4972 // ANIMATION_TYPE_START_DRAG
4973 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004974 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004975
4976 // ANIMATION_TYPE_SNAP_BACK
4977 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004978 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004979 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004980
4981 // ANIMATION_TYPE_ACTIVATED_CHILD
4982 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004983 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004984
4985 // ANIMATION_TYPE_DIMMED
4986 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004987 .animateDimmed(),
4988
4989 // ANIMATION_TYPE_CHANGE_POSITION
4990 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004991 .animateAlpha() // maybe the children change positions
4992 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004993 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004994 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004995 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004996 .animateZ(),
4997
4998 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004999 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005000
5001 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5002 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005003 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005004 .animateHeight()
5005 .animateTopInset()
5006 .animateY()
5007 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02005008 .animateZ()
5009 .hasDelays(),
5010
5011 // ANIMATION_TYPE_HIDE_SENSITIVE
5012 new AnimationFilter()
5013 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02005014
5015 // ANIMATION_TYPE_VIEW_RESIZE
5016 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005017 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02005018 .animateHeight()
5019 .animateTopInset()
5020 .animateY()
5021 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02005022
Selim Cinekb5605e52015-02-20 18:21:41 +01005023 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
5024 new AnimationFilter()
5025 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005026 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01005027 .animateHeight()
5028 .animateTopInset()
5029 .animateY()
5030 .animateZ(),
5031
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005032 // ANIMATION_TYPE_HEADS_UP_APPEAR
5033 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005034 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005035 .animateHeight()
5036 .animateTopInset()
5037 .animateY()
5038 .animateZ(),
5039
5040 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5041 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005042 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005043 .animateHeight()
5044 .animateTopInset()
5045 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07005046 .animateZ()
5047 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005048
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005049 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5050 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005051 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005052 .animateHeight()
5053 .animateTopInset()
5054 .animateY()
5055 .animateZ()
5056 .hasDelays(),
5057
Selim Cineka59ecc32015-04-07 10:51:49 -07005058 // ANIMATION_TYPE_HEADS_UP_OTHER
5059 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005060 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07005061 .animateHeight()
5062 .animateTopInset()
5063 .animateY()
5064 .animateZ(),
5065
Selim Cinekd9acca52014-09-01 22:33:25 +02005066 // ANIMATION_TYPE_EVERYTHING
5067 new AnimationFilter()
5068 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005069 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02005070 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02005071 .animateDimmed()
5072 .animateHideSensitive()
5073 .animateHeight()
5074 .animateTopInset()
5075 .animateY()
5076 .animateZ(),
Lucas Dupin4e023812018-04-02 21:19:23 -07005077
5078 // ANIMATION_TYPE_PULSE_APPEAR
5079 new AnimationFilter()
5080 .animateAlpha()
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005081 .hasDelays()
Lucas Dupin4e023812018-04-02 21:19:23 -07005082 .animateY(),
5083
5084 // ANIMATION_TYPE_PULSE_DISAPPEAR
5085 new AnimationFilter()
5086 .animateAlpha()
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005087 .hasDelays()
Lucas Dupin4e023812018-04-02 21:19:23 -07005088 .animateY(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005089 };
5090
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005091 static int[] LENGTHS = new int[] {
5092
5093 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005094 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005095
5096 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005097 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005098
5099 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5100 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5101
5102 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5103 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5104
5105 // ANIMATION_TYPE_START_DRAG
5106 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5107
5108 // ANIMATION_TYPE_SNAP_BACK
5109 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5110
5111 // ANIMATION_TYPE_ACTIVATED_CHILD
5112 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
5113
5114 // ANIMATION_TYPE_DIMMED
5115 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005116
5117 // ANIMATION_TYPE_CHANGE_POSITION
5118 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04005119
5120 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07005121 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005122
5123 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5124 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02005125
5126 // ANIMATION_TYPE_HIDE_SENSITIVE
5127 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02005128
5129 // ANIMATION_TYPE_VIEW_RESIZE
5130 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02005131
Selim Cinekb5605e52015-02-20 18:21:41 +01005132 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08005133 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01005134
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005135 // ANIMATION_TYPE_HEADS_UP_APPEAR
5136 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
5137
5138 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5139 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5140
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005141 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5142 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5143
Selim Cineka59ecc32015-04-07 10:51:49 -07005144 // ANIMATION_TYPE_HEADS_UP_OTHER
5145 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5146
Selim Cinekd9acca52014-09-01 22:33:25 +02005147 // ANIMATION_TYPE_EVERYTHING
5148 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Lucas Dupin4e023812018-04-02 21:19:23 -07005149
5150 // ANIMATION_TYPE_PULSE_APPEAR
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005151 StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR,
Lucas Dupin4e023812018-04-02 21:19:23 -07005152
5153 // ANIMATION_TYPE_PULSE_DISAPPEAR
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005154 StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR / 2,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005155 };
5156
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005157 static final int ANIMATION_TYPE_ADD = 0;
5158 static final int ANIMATION_TYPE_REMOVE = 1;
5159 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
5160 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
5161 static final int ANIMATION_TYPE_START_DRAG = 4;
5162 static final int ANIMATION_TYPE_SNAP_BACK = 5;
5163 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
5164 static final int ANIMATION_TYPE_DIMMED = 7;
5165 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04005166 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005167 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02005168 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02005169 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01005170 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005171 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
5172 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005173 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
5174 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
5175 static final int ANIMATION_TYPE_EVERYTHING = 18;
Lucas Dupin4e023812018-04-02 21:19:23 -07005176 static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
5177 static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005178
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005179 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
5180 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
5181
Selim Cinek572bbd42014-04-25 16:43:27 +02005182 final long eventStartTime;
5183 final View changingView;
5184 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005185 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005186 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005187 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005188 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07005189 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02005190
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005191 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005192 this(view, type, LENGTHS[type]);
5193 }
5194
Adrian Roos28f90c72017-05-08 17:24:26 -07005195 AnimationEvent(View view, int type, AnimationFilter filter) {
5196 this(view, type, LENGTHS[type], filter);
5197 }
5198
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005199 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07005200 this(view, type, length, FILTERS[type]);
5201 }
5202
5203 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02005204 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
5205 changingView = view;
5206 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005207 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07005208 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005209 }
5210
5211 /**
5212 * Combines the length of several animation events into a single value.
5213 *
5214 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005215 * @return The combined length. Depending on the event types, this might be the maximum of
5216 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005217 */
5218 static long combineLength(ArrayList<AnimationEvent> events) {
5219 long length = 0;
5220 int size = events.size();
5221 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005222 AnimationEvent event = events.get(i);
5223 length = Math.max(length, event.length);
5224 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
5225 return event.length;
5226 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005227 }
5228 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02005229 }
5230 }
Selim Cinek67b22602014-03-10 15:40:16 +01005231}