blob: a3d2423f5df1d356a7607b92df32bf5536ac199c [file] [log] [blame]
Selim Cinek67b22602014-03-10 15:40:16 +01001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.stack;
18
Selim Cinek614576e2016-01-20 10:54:09 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.animation.PropertyValuesHolder;
Selim Cinekd35c2792016-01-21 13:20:57 -080023import android.animation.TimeAnimator;
24import android.animation.ValueAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080025import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinek31d37b92016-04-26 09:56:42 -070026import android.annotation.FloatRange;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010027import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010028import android.content.Context;
29import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080030import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010031import android.graphics.Canvas;
Selim Cinek67b22602014-03-10 15:40:16 +010032import android.graphics.Paint;
Selim Cinek515b2032017-11-15 10:20:19 -080033import android.graphics.Path;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010034import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080035import android.graphics.PorterDuff;
36import android.graphics.PorterDuffXfermode;
37import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070038import android.os.Bundle;
Mady Mellor4b80b102016-01-22 08:03:58 -080039import android.os.Handler;
Mady Mellor95d743c2017-01-10 12:05:27 -080040import android.service.notification.StatusBarNotification;
Anthony Chen5e3742e2017-04-07 14:28:44 -070041import android.support.annotation.NonNull;
Selim Cinek8a9308d2017-08-24 09:31:08 -070042import android.support.annotation.VisibleForTesting;
Selim Cinek67b22602014-03-10 15:40:16 +010043import android.util.AttributeSet;
Selim Cinek972123d2016-05-03 14:25:58 -070044import android.util.FloatProperty;
Selim Cinek67b22602014-03-10 15:40:16 +010045import android.util.Log;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070046import android.util.Pair;
Selim Cinek972123d2016-05-03 14:25:58 -070047import android.util.Property;
Lucas Dupine17ce522017-07-17 15:45:06 -070048import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070049import android.view.InputDevice;
Selim Cinek67b22602014-03-10 15:40:16 +010050import android.view.MotionEvent;
51import android.view.VelocityTracker;
52import android.view.View;
53import android.view.ViewConfiguration;
54import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020055import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070056import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070057import android.view.accessibility.AccessibilityEvent;
58import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020059import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080060import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010061import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070062import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070063
Mady Mellora41587b2016-02-11 18:43:06 -080064import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010065import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Lucas Dupine17ce522017-07-17 15:45:06 -070066import com.android.settingslib.Utils;
Selim Cinek67b22602014-03-10 15:40:16 +010067import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080068import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010069import com.android.systemui.R;
70import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070071import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080072import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
73import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
74import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Selim Cineka32ab602014-06-11 15:06:01 +020075import com.android.systemui.statusbar.ActivatableNotificationView;
Dan Sandlereceda3d2014-07-21 15:35:01 -040076import com.android.systemui.statusbar.DismissView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020077import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010078import com.android.systemui.statusbar.ExpandableNotificationRow;
Selim Cinek515b2032017-11-15 10:20:19 -080079import com.android.systemui.statusbar.ExpandableOutlineView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020080import com.android.systemui.statusbar.ExpandableView;
Adrian Roos7d062c42017-03-30 15:11:43 -070081import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070082import com.android.systemui.statusbar.NotificationGuts;
Selim Cinek0cfbef42016-11-09 19:06:36 -080083import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070084import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010085import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020086import com.android.systemui.statusbar.StatusBarState;
Selim Cinek515b2032017-11-15 10:20:19 -080087import com.android.systemui.statusbar.notification.AnimatableProperty;
Selim Cinek33223572016-02-19 19:32:22 -080088import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080089import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cinek515b2032017-11-15 10:20:19 -080090import com.android.systemui.statusbar.notification.PropertyAnimator;
Selim Cineka7d4f822016-12-06 14:34:47 -080091import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Selim Cinekb5605e52015-02-20 18:21:41 +010092import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050093import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinekaac93252015-04-14 20:04:12 -070094import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070095import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010096import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +010097
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070098import android.support.v4.graphics.ColorUtils;
99
Selim Cinek707e2072017-06-30 18:32:40 +0200100import java.io.FileDescriptor;
101import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200102import java.util.ArrayList;
Adrian Roos7d062c42017-03-30 15:11:43 -0700103import java.util.Collection;
Selim Cinek33223572016-02-19 19:32:22 -0800104import java.util.Collections;
105import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200106import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800107import java.util.List;
Selim Cinek572bbd42014-04-25 16:43:27 +0200108
Selim Cinek67b22602014-03-10 15:40:16 +0100109/**
110 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
111 */
112public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200113 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800114 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
yoshiki iguchiff4696c2017-11-22 16:47:41 +0900115 NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider {
Selim Cinek67b22602014-03-10 15:40:16 +0100116
Selim Cinekd35c2792016-01-21 13:20:57 -0800117 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800118 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100119 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200120 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
121 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200122 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100123 /**
124 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
125 */
126 private static final int INVALID_POINTER = -1;
Selim Cinek515b2032017-11-15 10:20:19 -0800127 private static final AnimatableProperty SIDE_PADDINGS = AnimatableProperty.from(
128 "sidePaddings",
129 NotificationStackScrollLayout::setCurrentSidePadding,
130 NotificationStackScrollLayout::getCurrentSidePadding,
131 R.id.side_padding_animator_tag,
132 R.id.side_padding_animator_end_tag,
133 R.id.side_padding_animator_start_tag);
134 private static final AnimationProperties SIDE_PADDING_PROPERTIES =
135 new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
Selim Cinek67b22602014-03-10 15:40:16 +0100136
Selim Cinek1408eb52014-06-02 14:45:38 +0200137 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800138 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200139 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100140 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800141 private final Paint mBackgroundPaint = new Paint();
Selim Cinek515b2032017-11-15 10:20:19 -0800142 private final Path mBackgroundPath = new Path();
143 private final float[] mBackgroundRadii = new float[8];
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;
168 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100169 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800170 private int mIncreasedPaddingBetweenElements;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200171 private int mTopPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700172 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700173 private int mBottomInset = 0;
Selim Cinek515b2032017-11-15 10:20:19 -0800174 private float mCurrentSidePadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100175
176 /**
177 * The algorithm which calculates the properties for our children
178 */
Muyuan Li87798022016-04-07 17:51:25 -0700179 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100180
181 /**
182 * The current State this Layout is in
183 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200184 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700185 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100186 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800187 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700188 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
189 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
190 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
191 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
192 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200193 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700194 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
195 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200196 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200197 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200198 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800199 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200200
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200201 /**
202 * The raw amount of the overScroll on the top, which is not rubber-banded.
203 */
204 private float mOverScrolledTopPixels;
205
206 /**
207 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
208 */
209 private float mOverScrolledBottomPixels;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200210 private OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200211 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200212 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100213 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200214 private boolean mNeedsAnimation;
215 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200216 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200217 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400218 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100219 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200220 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200221 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200222 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200223 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200224 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700225 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200226 private boolean mExpandingNotification;
227 private boolean mExpandedInThisMotion;
Muyuan Li84b45612016-06-01 11:05:08 -0700228 protected boolean mScrollingEnabled;
Muyuan Li8cf63dc2016-05-18 17:40:58 -0700229 protected DismissView mDismissView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700230 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400231 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700232 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200233
234 /**
235 * Was the scroller scrolled to the top when the down motion was observed?
236 */
237 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200238 /**
239 * The minimal amount of over scroll which is needed in order to switch to the quick settings
240 * when over scrolling on a expanded card.
241 */
242 private float mMinTopOverScrollToEscape;
243 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700244 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200245 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200246 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700247 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200248 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100249 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700250 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700251
Selim Cinek1408eb52014-06-02 14:45:38 +0200252 /**
253 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
254 * This is needed to avoid scrolling too far after the notification was collapsed in the same
255 * motion.
256 */
257 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000258 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800259
Mady Mellor95d743c2017-01-10 12:05:27 -0800260 private NotificationMenuRowPlugin mCurrMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800261 private View mTranslatingParentView;
Mady Mellor95d743c2017-01-10 12:05:27 -0800262 private View mMenuExposedView;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700263 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200264
265 /**
266 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
267 * animating.
268 */
269 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700270 private boolean mDisallowDismissInThisMotion;
Jorim Jaggi56306252014-07-03 00:40:09 +0200271 private boolean mInterceptDelegateEnabled;
272 private boolean mDelegateToScrollView;
Selim Cineka59ecc32015-04-07 10:51:49 -0700273 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700274 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200275 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200276 = new ViewTreeObserver.OnPreDrawListener() {
277 @Override
278 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400279 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200280 updateChildren();
281 mChildrenUpdateRequested = false;
282 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200283 return true;
284 }
285 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500286 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100287 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100288 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700289 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek0fccc722015-07-29 17:04:36 -0700290 private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700291 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
292 = new HashSet<>();
293 private HeadsUpManager mHeadsUpManager;
294 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700295 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200296 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700297 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700298 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800299 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700300 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800301 = new ViewTreeObserver.OnPreDrawListener() {
302 @Override
303 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700304 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800305 return true;
306 }
307 };
308 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800309 private Rect mStartAnimationRect = new Rect();
310 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800311 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800312 private boolean mAnimateNextBackgroundBottom;
313 private boolean mAnimateNextBackgroundTop;
314 private ObjectAnimator mBottomAnimator = null;
315 private ObjectAnimator mTopAnimator = null;
316 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
317 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800318 private int mBgColor;
319 private float mDimAmount;
320 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800321 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800322 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
323 @Override
324 public void onAnimationEnd(Animator animation) {
325 mDimAnimator = null;
326 }
327 };
328 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
329 = new ValueAnimator.AnimatorUpdateListener() {
330
331 @Override
332 public void onAnimationUpdate(ValueAnimator animation) {
333 setDimAmount((Float) animation.getAnimatedValue());
334 }
335 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700336 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800337 private boolean mContinuousShadowUpdate;
338 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
339 = new ViewTreeObserver.OnPreDrawListener() {
340
341 @Override
342 public boolean onPreDraw() {
343 updateViewShadows();
344 return true;
345 }
346 };
347 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
348 @Override
349 public int compare(ExpandableView view, ExpandableView otherView) {
350 float endY = view.getTranslationY() + view.getActualHeight();
351 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
352 if (endY < otherEndY) {
353 return -1;
354 } else if (endY > otherEndY) {
355 return 1;
356 } else {
357 // The two notifications end at the same location
358 return 0;
359 }
360 }
361 };
Selim Cinek25503252016-03-03 15:31:43 -0800362 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
Adrian Roos7d062c42017-03-30 15:11:43 -0700363 private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700364 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700365 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800366 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700367 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700368 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400369 private View mForcedScroll;
Selim Cinek972123d2016-05-03 14:25:58 -0700370 private float mBackgroundFadeAmount = 1.0f;
371 private static final Property<NotificationStackScrollLayout, Float> BACKGROUND_FADE =
372 new FloatProperty<NotificationStackScrollLayout>("backgroundFade") {
373 @Override
374 public void setValue(NotificationStackScrollLayout object, float value) {
375 object.setBackgroundFadeAmount(value);
376 }
377
378 @Override
379 public Float get(NotificationStackScrollLayout object) {
380 return object.getBackgroundFadeAmount();
381 }
382 };
Lucas Dupine17ce522017-07-17 15:45:06 -0700383 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700384 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700385 private boolean mForwardScrollable;
386 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700387 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700388 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800389 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700390 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800391 private boolean mNoAmbient;
392 private final Rect mClipRect = new Rect();
393 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700394 private Rect mRequestedClipBounds;
395 private boolean mInHeadsUpPinnedMode;
396 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800397 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100398 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700399 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800400 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek0fe07392017-11-09 13:26:34 -0800401 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800402 private int mLockscreenSidePaddings;
403 private int mSidePaddings;
Selim Cinek67b22602014-03-10 15:40:16 +0100404
405 public NotificationStackScrollLayout(Context context) {
406 this(context, null);
407 }
408
409 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
410 this(context, attrs, 0);
411 }
412
413 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
414 this(context, attrs, defStyleAttr, 0);
415 }
416
417 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
418 int defStyleRes) {
419 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800420 Resources res = getResources();
421
Selim Cinek281c2022016-10-13 19:14:43 -0700422 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800423 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800424 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
425 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200426 mExpandHelper = new ExpandHelper(getContext(), this,
427 minHeight, maxHeight);
428 mExpandHelper.setEventSource(this);
429 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800430 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Muyuan Li333a4fc2016-04-16 17:13:46 -0700431 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100432 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800433 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800434 mShouldDrawNotificationBackground =
435 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700436 mFadeNotificationsOnDismiss =
437 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800438
439 updateWillNotDraw();
Selim Cinek515b2032017-11-15 10:20:19 -0800440 mBackgroundPaint.setAntiAlias(true);
441 mBackgroundPaint.setStyle(Paint.Style.FILL);
Selim Cinek67b22602014-03-10 15:40:16 +0100442 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100443 mDebugPaint = new Paint();
444 mDebugPaint.setColor(0xffff0000);
445 mDebugPaint.setStrokeWidth(2);
446 mDebugPaint.setStyle(Paint.Style.STROKE);
447 }
448 }
449
Mady Mellor95d743c2017-01-10 12:05:27 -0800450 public NotificationSwipeActionHelper getSwipeActionHelper() {
451 return mSwipeHelper;
452 }
453
Selim Cinek67b22602014-03-10 15:40:16 +0100454 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800455 public void onMenuClicked(View view, int x, int y, MenuItem item) {
456 if (mLongPressListener == null) {
457 return;
458 }
459 if (view instanceof ExpandableNotificationRow) {
460 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800461 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
462 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800463 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800464 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800465 }
466
467 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800468 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700469 if (mTranslatingParentView != null && row == mTranslatingParentView) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800470 mMenuExposedView = null;
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700471 mTranslatingParentView = null;
472 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000473 }
474
475 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800476 public void onMenuShown(View row) {
477 mMenuExposedView = mTranslatingParentView;
478 if (row instanceof ExpandableNotificationRow) {
479 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
480 ((ExpandableNotificationRow) row).getStatusBarNotification()
481 .getPackageName());
482 }
483 mSwipeHelper.onMenuShown(row);
484 }
485
Selim Cinek67b22602014-03-10 15:40:16 +0100486 protected void onDraw(Canvas canvas) {
Adrian Roosf0b4f962017-05-25 11:53:11 -0700487 if (mShouldDrawNotificationBackground && !mAmbientState.isDark()
488 && mCurrentBounds.top < mCurrentBounds.bottom) {
Selim Cinek515b2032017-11-15 10:20:19 -0800489 canvas.drawPath(mBackgroundPath, mBackgroundPaint);
Selim Cinekd381bc32016-08-15 12:40:57 -0700490 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800491
Selim Cinek67b22602014-03-10 15:40:16 +0100492 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800493 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100494 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800495 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100496 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200497 y = getHeight() - getEmptyBottomMargin();
498 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100499 }
500 }
501
Selim Cinekd35c2792016-01-21 13:20:57 -0800502 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800503 // No need to update the background color if it's not being drawn.
504 if (!mShouldDrawNotificationBackground) {
505 return;
506 }
507
Selim Cinekd35c2792016-01-21 13:20:57 -0800508 float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Selim Cinek972123d2016-05-03 14:25:58 -0700509 alpha *= mBackgroundFadeAmount;
Selim Cinekd35c2792016-01-21 13:20:57 -0800510 // We need to manually blend in the background color
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700511 int scrimColor = mScrimController.getBackgroundColor();
512 int color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100513 if (mCachedBackgroundColor != color) {
514 mCachedBackgroundColor = color;
515 mBackgroundPaint.setColor(color);
516 invalidate();
517 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800518 }
519
Selim Cinek67b22602014-03-10 15:40:16 +0100520 private void initView(Context context) {
521 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100522 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200523 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100524 final ViewConfiguration configuration = ViewConfiguration.get(context);
525 mTouchSlop = configuration.getScaledTouchSlop();
526 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
527 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100528 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700529
530 Resources res = context.getResources();
531 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800532 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700533 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700534 mPaddingBetweenElements = Math.max(1,
535 res.getDimensionPixelSize(R.dimen.notification_divider_height));
536 mIncreasedPaddingBetweenElements =
537 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
538 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200539 R.dimen.min_top_overscroll_to_qs);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700540 mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
541 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinek515b2032017-11-15 10:20:19 -0800542 mLockscreenSidePaddings = res.getDimensionPixelSize(
543 R.dimen.notification_lockscreen_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700544 mMinInteractionHeight = res.getDimensionPixelSize(
545 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800546 mCornerRadius = res.getDimensionPixelSize(
547 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cineka5eaa602014-05-12 21:27:47 +0200548 }
549
Selim Cinek25503252016-03-03 15:31:43 -0800550 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700551 mDrawBackgroundAsSrc = asSrc;
552 updateSrcDrawing();
553 }
554
555 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800556 if (!mShouldDrawNotificationBackground) {
557 return;
558 }
559
Selim Cinek48ff9b42016-11-09 19:31:51 -0800560 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700561 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800562 invalidate();
563 }
564
Selim Cinekaef92ef2014-06-06 18:06:04 +0200565 private void notifyHeightChangeListener(ExpandableView view) {
566 if (mOnHeightChangedListener != null) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100567 mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200568 }
Selim Cinek67b22602014-03-10 15:40:16 +0100569 }
570
571 @Override
572 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
573 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400574 // We need to measure all children even the GONE ones, such that the heights are calculated
575 // correctly as they are used to calculate how many we can fit on the screen.
576 final int size = getChildCount();
577 for (int i = 0; i < size; i++) {
Anthony Chen04d1ea72017-04-21 14:30:11 -0700578 measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400579 }
Selim Cinek67b22602014-03-10 15:40:16 +0100580 }
581
582 @Override
583 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100584 // we layout all our children centered on the top
585 float centerX = getWidth() / 2.0f;
586 for (int i = 0; i < getChildCount(); i++) {
587 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400588 // We need to layout all children even the GONE ones, such that the heights are
589 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100590 float width = child.getMeasuredWidth();
591 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100592 child.layout((int) (centerX - width / 2.0f),
593 0,
594 (int) (centerX + width / 2.0f),
595 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100596 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200597 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100598 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200599 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200600 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800601 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700602 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100603 }
604
Selim Cinek5bc852a2015-12-21 12:19:09 -0800605 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
606 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200607 mNeedViewResizeAnimation = true;
608 mNeedsAnimation = true;
609 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200610 }
611
Selim Cinekdb167372016-11-17 15:41:17 -0800612 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
613 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800614 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200615 }
616
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200617 public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) {
618 mListener = listener;
619 }
620
Selim Cineka7d4f822016-12-06 14:34:47 -0800621 @Override
622 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
623 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200624 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800625 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200626 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100627 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800628 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100629 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800630 if (row.getVisibility() != View.VISIBLE) {
631 return false;
632 }
633 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200634 }
635
Selim Cinek67b22602014-03-10 15:40:16 +0100636 private void setMaxLayoutHeight(int maxLayoutHeight) {
637 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700638 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200639 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100640 }
641
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200642 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700643 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700644 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700645 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100646 }
647
Selim Cinekbc243a92016-09-27 16:35:13 -0700648 private void updateAlgorithmLayoutMinHeight() {
649 mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
650 }
651
Selim Cinek67b22602014-03-10 15:40:16 +0100652 /**
653 * Updates the children views according to the stack scroll algorithm. Call this whenever
654 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
655 */
656 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800657 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800658 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
659 ? 0
660 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200661 mAmbientState.setScrollY(mOwnScrollY);
662 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200663 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200664 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100665 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200666 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100667 }
668 }
669
Selim Cinekc383fd02016-10-21 15:31:26 -0700670 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700671 mShelf.updateAppearance();
Selim Cinekc383fd02016-10-21 15:31:26 -0700672 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
673 updateBackground();
674 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700675 }
676
Selim Cinek3776fe02016-02-04 13:32:43 -0800677 private void updateScrollStateForAddedChildren() {
678 if (mChildrenToAddAnimated.isEmpty()) {
679 return;
680 }
681 for (int i = 0; i < getChildCount(); i++) {
682 ExpandableView child = (ExpandableView) getChildAt(i);
683 if (mChildrenToAddAnimated.contains(child)) {
684 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800685 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
686 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
687 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800688 int childHeight = getIntrinsicHeight(child) + padding;
689 if (startingPosition < mOwnScrollY) {
690 // This child starts off screen, so let's keep it offscreen to keep the others visible
691
Selim Cinekef406062016-09-29 17:33:13 -0700692 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800693 }
694 }
695 }
696 clampScrollPosition();
697 }
698
Adrian Roos181385c2016-05-05 17:45:44 -0400699 private void updateForcedScroll() {
700 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
701 || !mForcedScroll.isAttachedToWindow())) {
702 mForcedScroll = null;
703 }
704 if (mForcedScroll != null) {
705 ExpandableView expandableView = (ExpandableView) mForcedScroll;
706 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
707 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700708 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400709
710 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700711
712 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
713 // that it is not visible anymore.
714 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700715 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400716 }
717 }
718 }
719
Selim Cinek319bdc42014-05-01 23:01:58 +0200720 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200721 if (!mChildrenUpdateRequested) {
722 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
723 mChildrenUpdateRequested = true;
724 invalidate();
725 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200726 }
727
Selim Cinek67b22602014-03-10 15:40:16 +0100728 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200729 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100730 }
731
Selim Cinekf7a14c02014-07-07 14:01:46 +0200732 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100733 int scrollRange = getScrollRange();
734 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700735 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100736 }
737 }
738
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200739 public int getTopPadding() {
740 return mTopPadding;
741 }
742
Selim Cinek1408eb52014-06-02 14:45:38 +0200743 private void setTopPadding(int topPadding, boolean animate) {
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200744 if (mTopPadding != topPadding) {
745 mTopPadding = topPadding;
746 updateAlgorithmHeightAndPadding();
747 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200748 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200749 mTopPaddingNeedsAnimation = true;
750 mNeedsAnimation = true;
751 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200752 requestChildrenUpdate();
Selim Cinekaef92ef2014-06-06 18:06:04 +0200753 notifyHeightChangeListener(null);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200754 }
755 }
756
757 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700758 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200759 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700760 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200761 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700762 public void setExpandedHeight(float height) {
763 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700764 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800765 int minExpansionHeight = getMinExpansionHeight();
766 if (height < minExpansionHeight) {
767 mClipRect.left = 0;
768 mClipRect.right = getWidth();
769 mClipRect.top = 0;
770 mClipRect.bottom = (int) height;
771 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700772 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800773 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700774 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800775 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200776 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700777 float translationY;
778 float appearEndPosition = getAppearEndPosition();
779 float appearStartPosition = getAppearStartPosition();
780 if (height >= appearEndPosition) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700781 translationY = 0;
Selim Cinek94c2d822016-07-13 18:50:04 -0700782 stackHeight = (int) height;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200783 } else {
Selim Cinek94c2d822016-07-13 18:50:04 -0700784 float appearFraction = getAppearFraction(height);
785 if (appearFraction >= 0) {
786 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
787 appearFraction);
788 } else {
789 // This may happen when pushing up a heads up. We linearly push it up from the
790 // start
791 translationY = height - appearStartPosition + getExpandTranslationStart();
792 }
793 stackHeight = (int) (height - translationY);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200794 }
795 if (stackHeight != mCurrentStackHeight) {
796 mCurrentStackHeight = stackHeight;
797 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200798 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200799 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700800 setStackTranslation(translationY);
Selim Cinekcafa87f2016-10-26 17:00:17 -0700801 }
802
803 private void setRequestedClipBounds(Rect clipRect) {
804 mRequestedClipBounds = clipRect;
805 updateClipping();
806 }
807
808 public void updateClipping() {
809 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
810 && !mHeadsUpAnimatingAway;
811 if (mIsClipped != clipped) {
812 mIsClipped = clipped;
813 updateFadingState();
814 }
815 if (clipped) {
816 setClipBounds(mRequestedClipBounds);
817 } else {
818 setClipBounds(null);
819 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700820 }
821
822 /**
823 * @return The translation at the beginning when expanding.
824 * Measured relative to the resting position.
825 */
826 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800827 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700828 }
829
830 /**
831 * @return the position from where the appear transition starts when expanding.
832 * Measured in absolute height.
833 */
834 private float getAppearStartPosition() {
Selim Cinekd127d792016-11-01 19:11:41 -0700835 if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
Selim Cinekebf42342017-07-13 15:46:10 +0200836 if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) {
Selim Cinekd127d792016-11-01 19:11:41 -0700837 // If we ever expanded beyond the first notification, it's allowed to merge into
838 // the shelf
839 return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
840 }
841 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800842 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700843 }
844
845 /**
846 * @return the position from where the appear transition ends when expanding.
847 * Measured in absolute height.
848 */
849 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -0700850 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -0700851 int notGoneChildCount = getNotGoneChildCount();
852 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100853 int minNotificationsForShelf = 1;
Selim Cinekebf42342017-07-13 15:46:10 +0200854 if (mTrackingHeadsUp
855 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100856 appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
857 minNotificationsForShelf = 2;
858 } else {
859 appearPosition = 0;
860 }
Selim Cinek66440cf2017-05-26 13:48:47 -0700861 if (notGoneChildCount >= minNotificationsForShelf
Anthony Chen9e05d462017-04-07 10:10:21 -0700862 && mShelf.getVisibility() != GONE) {
Selim Cinekcde90e52016-12-22 21:01:49 +0100863 appearPosition += mShelf.getIntrinsicHeight();
864 }
Selim Cinekaa417da2016-10-27 18:17:08 -0700865 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +0100866 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -0700867 }
868 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -0700869 }
870
871 /**
872 * @param height the height of the panel
873 * @return the fraction of the appear animation that has been performed
874 */
875 public float getAppearFraction(float height) {
876 float appearEndPosition = getAppearEndPosition();
877 float appearStartPosition = getAppearStartPosition();
878 return (height - appearStartPosition)
879 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700880 }
881
Selim Cinekd2281152015-04-10 14:37:46 -0700882 public float getStackTranslation() {
883 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700884 }
885
Selim Cinekd2281152015-04-10 14:37:46 -0700886 private void setStackTranslation(float stackTranslation) {
887 if (stackTranslation != mStackTranslation) {
888 mStackTranslation = stackTranslation;
889 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700890 requestChildrenUpdate();
891 }
Selim Cinek67b22602014-03-10 15:40:16 +0100892 }
893
894 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100895 * 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 +0100896 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
897 *
898 * @return either the layout height or the externally defined height, whichever is smaller
899 */
Selim Cinek343e6e22014-04-11 21:23:30 +0200900 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +0100901 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
902 }
903
Selim Cinek816c8e42015-11-19 12:00:45 -0800904 public int getFirstItemMinHeight() {
905 final ExpandableView firstChild = getFirstChildNotGone();
906 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100907 }
908
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000909 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
Dan Sandler4247a5c2014-07-23 15:58:08 -0400910 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +0100911 }
912
Jason Monk16ac3772016-02-10 15:39:21 -0500913 public void setQsContainer(ViewGroup qsContainer) {
914 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +0200915 }
916
Mady Mellorb0a82462016-04-30 17:31:02 -0700917 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100918 public void onChildDismissed(View v) {
Selim Cinekd1395642016-04-28 12:22:42 -0700919 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
920 if (!row.isDismissed()) {
921 handleChildDismissed(v);
922 }
923 ViewGroup transientContainer = row.getTransientContainer();
924 if (transientContainer != null) {
925 transientContainer.removeTransientView(v);
926 }
927 }
928
929 private void handleChildDismissed(View v) {
Dan Sandlereceda3d2014-07-21 15:35:01 -0400930 if (mDismissAllInProgress) {
931 return;
932 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100933 setSwipingInProgress(false);
Selim Cinekeb973562014-05-02 17:07:49 +0200934 if (mDragAnimPendingChildren.contains(v)) {
935 // We start the swipe and finish it in the same frame, we don't want any animation
936 // for the drag
937 mDragAnimPendingChildren.remove(v);
938 }
Selim Cinek572bbd42014-04-25 16:43:27 +0200939 mSwipedOutViews.add(v);
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200940 mAmbientState.onDragFinished(v);
Selim Cinek33223572016-02-19 19:32:22 -0800941 updateContinuousShadowDrawing();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700942 if (v instanceof ExpandableNotificationRow) {
943 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
944 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -0700945 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700946 }
947 }
Selim Cineke9bad242016-06-15 11:46:37 -0700948 performDismiss(v, mGroupManager, false /* fromAccessibility */);
Blazej Magnowski72323322015-07-24 11:49:40 -0700949
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700950 mFalsingManager.onNotificationDismissed();
951 if (mFalsingManager.shouldEnforceBouncer()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500952 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
Felipe Lemeee5d6302016-04-22 16:11:19 -0700953 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -0700954 }
Selim Cinekeb973562014-05-02 17:07:49 +0200955 }
956
Selim Cineke9bad242016-06-15 11:46:37 -0700957 public static void performDismiss(View v, NotificationGroupManager groupManager,
958 boolean fromAccessibility) {
Selim Cinek9e624e72016-07-20 13:46:49 -0700959 if (!(v instanceof ExpandableNotificationRow)) {
960 return;
Selim Cinek2a739342016-03-17 10:28:55 -0700961 }
Selim Cinek9e624e72016-07-20 13:46:49 -0700962 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
963 if (groupManager.isOnlyChildInGroup(row.getStatusBarNotification())) {
964 ExpandableNotificationRow groupSummary =
965 groupManager.getLogicalGroupSummary(row.getStatusBarNotification());
966 if (groupSummary.isClearable()) {
967 performDismiss(groupSummary, groupManager, fromAccessibility);
968 }
969 }
970 row.setDismissed(true, fromAccessibility);
971 if (row.isClearable()) {
972 row.performDismiss();
Selim Cinek2a739342016-03-17 10:28:55 -0700973 }
974 if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
975 }
976
Selim Cinekeb973562014-05-02 17:07:49 +0200977 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -0800978 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200979 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -0800980 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +0200981 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +0200982 if (mAnimationsEnabled) {
983 mSnappedBackChildren.add(animView);
984 mNeedsAnimation = true;
985 }
Selim Cinekeb973562014-05-02 17:07:49 +0200986 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +0200987 } else {
988 // We start the swipe and snap back in the same frame, we don't want any animation
989 mDragAnimPendingChildren.remove(animView);
990 }
Mady Mellor95d743c2017-01-10 12:05:27 -0800991 if (mCurrMenuRow != null && targetLeft == 0) {
992 mCurrMenuRow.resetMenu();
993 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -0800994 }
Selim Cinek67b22602014-03-10 15:40:16 +0100995 }
996
Adrian Roos5d9cc662014-05-28 17:08:13 +0200997 @Override
998 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Selim Cinek131c1e22015-05-11 19:04:49 -0700999 if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
Selim Cinekaac93252015-04-14 20:04:12 -07001000 mScrimController.setTopHeadsUpDragAmount(animView,
Winsonbde852d2016-04-15 19:06:54 -07001001 Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
Selim Cinekaac93252015-04-14 20:04:12 -07001002 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001003 // Returning true prevents alpha fading.
1004 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +02001005 }
1006
Mady Mellorb0a82462016-04-30 17:31:02 -07001007 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001008 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001009 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001010 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001011 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -08001012 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -07001013 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001014 mDragAnimPendingChildren.add(v);
1015 mNeedsAnimation = true;
1016 }
Selim Cinekeb973562014-05-02 17:07:49 +02001017 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +01001018 }
1019
Selim Cinek684a4422015-04-15 16:18:39 -07001020 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001021 if (v instanceof ExpandableNotificationRow) {
1022 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001023 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001024 }
1025 return false;
1026 }
1027
1028 private boolean isHeadsUp(View v) {
1029 if (v instanceof ExpandableNotificationRow) {
1030 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1031 return row.isHeadsUp();
1032 }
1033 return false;
1034 }
1035
Mady Mellorb0a82462016-04-30 17:31:02 -07001036 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001037 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001038 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001039 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001040 }
1041
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001042 @Override
1043 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001044 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001045 }
1046
Mady Mellor4b80b102016-01-22 08:03:58 -08001047 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001048 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001049 View child = getChildAtPosition(ev.getX(), ev.getY());
1050 if (child instanceof ExpandableNotificationRow) {
1051 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1052 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001053 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001054 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001055 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001056 || (parent.getNotificationChildren().size() == 1
1057 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001058 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001059 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001060 // child notifications so we use the parent of the child. We also do the same
1061 // if we only have a single child.
1062 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001063 }
1064 }
1065 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001066 }
1067
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001068 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1069 getLocationOnScreen(mTempInt2);
1070 float localTouchY = touchY - mTempInt2[1];
1071
1072 ExpandableView closestChild = null;
1073 float minDist = Float.MAX_VALUE;
1074
1075 // find the view closest to the location, accounting for GONE views
1076 final int count = getChildCount();
1077 for (int childIdx = 0; childIdx < count; childIdx++) {
1078 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1079 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001080 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001081 continue;
1082 }
1083 float childTop = slidingChild.getTranslationY();
1084 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001085 float bottom = childTop + slidingChild.getActualHeight()
1086 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001087
1088 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1089 if (dist < minDist) {
1090 closestChild = slidingChild;
1091 minDist = dist;
1092 }
1093 }
1094 return closestChild;
1095 }
1096
Mady Mellorb0a82462016-04-30 17:31:02 -07001097 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001098 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001099 getLocationOnScreen(mTempInt2);
1100 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001101 }
1102
Mady Mellorb0a82462016-04-30 17:31:02 -07001103 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001104 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001105 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1106
1107 }
1108
1109 /**
1110 * Get the child at a certain screen location.
1111 *
1112 * @param touchX the x coordinate
1113 * @param touchY the y coordinate
1114 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1115 * @return the child at the given location.
1116 */
1117 private ExpandableView getChildAtPosition(float touchX, float touchY,
1118 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001119 // find the view under the pointer, accounting for GONE views
1120 final int count = getChildCount();
1121 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001122 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001123 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001124 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001125 continue;
1126 }
Selim Cinek89faff12014-06-19 16:29:04 -07001127 float childTop = slidingChild.getTranslationY();
1128 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001129 float bottom = childTop + slidingChild.getActualHeight()
1130 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001131
1132 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1133 // camera affordance).
1134 int left = 0;
1135 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001136
Selim Cinek34ed7c02017-09-08 15:03:12 -07001137 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001138 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001139 if (slidingChild instanceof ExpandableNotificationRow) {
1140 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001141 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Selim Cinek5bc852a2015-12-21 12:19:09 -08001142 && mHeadsUpManager.getTopEntry().entry.row != row
1143 && mGroupManager.getGroupSummary(
1144 mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
1145 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001146 continue;
1147 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001148 return row.getViewAtPosition(touchY - childTop);
1149 }
Selim Cinek67b22602014-03-10 15:40:16 +01001150 return slidingChild;
1151 }
1152 }
1153 return null;
1154 }
1155
Mady Mellorb0a82462016-04-30 17:31:02 -07001156 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001157 public boolean canChildBeExpanded(View v) {
1158 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001159 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001160 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001161 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001162 }
1163
Chris Wren6abeeb92016-05-26 14:44:38 -04001164 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001165 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001166 public void setUserExpandedChild(View v, boolean userExpanded) {
1167 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001168 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001169 if (userExpanded && onKeyguard()) {
1170 // Due to a race when locking the screen while touching, a notification may be
1171 // expanded even after we went back to keyguard. An example of this happens if
1172 // you click in the empty space while expanding a group.
1173
1174 // We also need to un-user lock it here, since otherwise the content height
1175 // calculated might be wrong. We also can't invert the two calls since
1176 // un-userlocking it will trigger a layout switch in the content view.
1177 row.setUserLocked(false);
1178 updateContentHeight();
1179 notifyHeightChangeListener(row);
1180 return;
1181 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001182 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1183 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001184 }
1185 }
1186
Mady Mellorb0a82462016-04-30 17:31:02 -07001187 @Override
1188 public void setExpansionCancelled(View v) {
1189 if (v instanceof ExpandableNotificationRow) {
1190 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1191 }
1192 }
1193
1194 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001195 public void setUserLockedChild(View v, boolean userLocked) {
1196 if (v instanceof ExpandableNotificationRow) {
1197 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1198 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001199 cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02001200 requestDisallowInterceptTouchEvent(true);
1201 }
1202
1203 @Override
1204 public void expansionStateChanged(boolean isExpanding) {
1205 mExpandingNotification = isExpanding;
1206 if (!mExpandedInThisMotion) {
1207 mMaxScrollAfterExpand = mOwnScrollY;
1208 mExpandedInThisMotion = true;
1209 }
1210 }
1211
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001212 @Override
1213 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001214 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001215 }
1216
Selim Cinek1408eb52014-06-02 14:45:38 +02001217 public void setScrollingEnabled(boolean enable) {
1218 mScrollingEnabled = enable;
1219 }
1220
Adrian Roos181385c2016-05-05 17:45:44 -04001221 public void lockScrollTo(View v) {
1222 if (mForcedScroll == v) {
1223 return;
1224 }
1225 mForcedScroll = v;
1226 scrollTo(v);
1227 }
1228
Ricky Waicd35def2016-05-03 11:07:07 +01001229 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001230 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001231 int positionInLinearLayout = getPositionInLinearLayout(v);
1232 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1233 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001234
Adrian Roos4a579672016-05-24 16:54:37 -07001235 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1236 // that it is not visible anymore.
1237 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001238 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1239 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001240 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001241 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001242 }
Ricky Waicd35def2016-05-03 11:07:07 +01001243 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001244 }
1245
Adrian Roos181385c2016-05-05 17:45:44 -04001246 /**
1247 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1248 * the IME.
1249 */
1250 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1251 return positionInLinearLayout + v.getIntrinsicHeight() +
1252 getImeInset() - getHeight() + getTopPadding();
1253 }
1254
Adrian Roos5153d4a2016-03-22 10:01:56 -07001255 @Override
1256 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001257 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001258
1259 int range = getScrollRange();
1260 if (mOwnScrollY > range) {
1261 // HACK: We're repeatedly getting staggered insets here while the IME is
1262 // animating away. To work around that we'll wait until things have settled.
1263 removeCallbacks(mReclamp);
1264 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001265 } else if (mForcedScroll != null) {
1266 // The scroll was requested before we got the actual inset - in case we need
1267 // to scroll up some more do so now.
1268 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001269 }
1270 return insets;
1271 }
1272
1273 private Runnable mReclamp = new Runnable() {
1274 @Override
1275 public void run() {
1276 int range = getScrollRange();
1277 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1278 mDontReportNextOverScroll = true;
1279 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001280 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001281 }
1282 };
1283
Selim Cinek1408eb52014-06-02 14:45:38 +02001284 public void setExpandingEnabled(boolean enable) {
1285 mExpandHelper.setEnabled(enable);
1286 }
1287
1288 private boolean isScrollingEnabled() {
1289 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001290 }
1291
Mady Mellorb0a82462016-04-30 17:31:02 -07001292 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001293 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001294 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001295 }
1296
Selim Cinek19c8c702014-08-25 22:09:19 +02001297 @Override
1298 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001299 return onKeyguard();
1300 }
1301
1302 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001303 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001304 }
1305
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001306 private void setSwipingInProgress(boolean isSwiped) {
1307 mSwipingInProgress = isSwiped;
1308 if(isSwiped) {
1309 requestDisallowInterceptTouchEvent(true);
1310 }
Selim Cinek67b22602014-03-10 15:40:16 +01001311 }
1312
1313 @Override
1314 protected void onConfigurationChanged(Configuration newConfig) {
1315 super.onConfigurationChanged(newConfig);
1316 float densityScale = getResources().getDisplayMetrics().density;
1317 mSwipeHelper.setDensityScale(densityScale);
1318 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1319 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1320 initView(getContext());
1321 }
1322
Dan Sandlereceda3d2014-07-21 15:35:01 -04001323 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001324 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1325 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001326 }
1327
Mady Mellor86889c22016-04-18 16:37:06 -07001328 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001329 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001330 // If the child is showing the notification menu snap to that
1331 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001332 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001333 }
1334
Selim Cinek67b22602014-03-10 15:40:16 +01001335 @Override
1336 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001337 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1338 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001339 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001340 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001341 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001342 if (isCancelOrUp) {
1343 mExpandHelper.onlyObserveMovements(false);
1344 }
1345 boolean wasExpandingBefore = mExpandingNotification;
1346 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001347 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1348 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001349 dispatchDownEventToScroller(ev);
1350 }
1351 }
Selim Cinek67b22602014-03-10 15:40:16 +01001352 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001353 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1354 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001355 scrollerWantsIt = onScrollTouch(ev);
1356 }
1357 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001358 if (!mIsBeingDragged
1359 && !mExpandingNotification
1360 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001361 && !mOnlyScrollingInThisMotion
1362 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001363 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1364 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001365
1366 // Check if we need to clear any snooze leavebehinds
Eliot Courtney47098cb2017-10-18 17:30:30 +09001367 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07001368 if (guts != null && !isTouchInView(ev, guts)
1369 && guts.getGutsContent() instanceof NotificationSnooze) {
1370 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1371 if ((ns.isExpanded() && isCancelOrUp)
1372 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1373 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1374 // clear it on the next non-horizontal swipe or expand event.
1375 checkSnoozeLeavebehind();
1376 }
1377 }
1378 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1379 mCheckForLeavebehind = true;
1380 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001381 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1382 }
1383
1384 private void dispatchDownEventToScroller(MotionEvent ev) {
1385 MotionEvent downEvent = MotionEvent.obtain(ev);
1386 downEvent.setAction(MotionEvent.ACTION_DOWN);
1387 onScrollTouch(downEvent);
1388 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001389 }
1390
Selim Cinek11e33232016-08-05 15:30:53 -07001391 @Override
1392 public boolean onGenericMotionEvent(MotionEvent event) {
1393 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1394 || mDisallowScrollingInThisMotion) {
1395 return false;
1396 }
1397 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1398 switch (event.getAction()) {
1399 case MotionEvent.ACTION_SCROLL: {
1400 if (!mIsBeingDragged) {
1401 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1402 if (vscroll != 0) {
1403 final int delta = (int) (vscroll * getVerticalScrollFactor());
1404 final int range = getScrollRange();
1405 int oldScrollY = mOwnScrollY;
1406 int newScrollY = oldScrollY - delta;
1407 if (newScrollY < 0) {
1408 newScrollY = 0;
1409 } else if (newScrollY > range) {
1410 newScrollY = range;
1411 }
1412 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001413 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001414 return true;
1415 }
1416 }
1417 }
1418 }
1419 }
1420 }
1421 return super.onGenericMotionEvent(event);
1422 }
1423
Selim Cinek67b22602014-03-10 15:40:16 +01001424 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001425 if (!isScrollingEnabled()) {
1426 return false;
1427 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001428 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001429 return false;
1430 }
Adrian Roos181385c2016-05-05 17:45:44 -04001431 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001432 initVelocityTrackerIfNotExists();
1433 mVelocityTracker.addMovement(ev);
1434
1435 final int action = ev.getAction();
1436
1437 switch (action & MotionEvent.ACTION_MASK) {
1438 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001439 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001440 return false;
1441 }
1442 boolean isBeingDragged = !mScroller.isFinished();
1443 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001444 /*
1445 * If being flinged and user touches, stop the fling. isFinished
1446 * will be false if being flinged.
1447 */
1448 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001449 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001450 }
1451
1452 // Remember where the motion event started
1453 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001454 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001455 mActivePointerId = ev.getPointerId(0);
1456 break;
1457 }
1458 case MotionEvent.ACTION_MOVE:
1459 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1460 if (activePointerIndex == -1) {
1461 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1462 break;
1463 }
1464
1465 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001466 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001467 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001468 final int xDiff = Math.abs(x - mDownX);
1469 final int yDiff = Math.abs(deltaY);
1470 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001471 setIsBeingDragged(true);
1472 if (deltaY > 0) {
1473 deltaY -= mTouchSlop;
1474 } else {
1475 deltaY += mTouchSlop;
1476 }
1477 }
1478 if (mIsBeingDragged) {
1479 // Scroll to follow the motion event
1480 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001481 int range = getScrollRange();
1482 if (mExpandedInThisMotion) {
1483 range = Math.min(range, mMaxScrollAfterExpand);
1484 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001485
1486 float scrollAmount;
1487 if (deltaY < 0) {
1488 scrollAmount = overScrollDown(deltaY);
1489 } else {
1490 scrollAmount = overScrollUp(deltaY, range);
1491 }
Selim Cinek67b22602014-03-10 15:40:16 +01001492
Selim Cinek9212de82017-02-06 16:04:28 -08001493 // Calling customOverScrollBy will call onCustomOverScrolled, which
1494 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001495 if (scrollAmount != 0.0f) {
1496 // The scrolling motion could not be compensated with the
1497 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001498 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1499 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001500 // If we're scrolling, leavebehinds should be dismissed
1501 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001502 }
Selim Cinek67b22602014-03-10 15:40:16 +01001503 }
1504 break;
1505 case MotionEvent.ACTION_UP:
1506 if (mIsBeingDragged) {
1507 final VelocityTracker velocityTracker = mVelocityTracker;
1508 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1509 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1510
Selim Cinek1408eb52014-06-02 14:45:38 +02001511 if (shouldOverScrollFling(initialVelocity)) {
1512 onOverScrollFling(true, initialVelocity);
1513 } else {
1514 if (getChildCount() > 0) {
1515 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1516 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1517 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1518 fling(-initialVelocity);
1519 } else {
1520 onOverScrollFling(false, initialVelocity);
1521 }
1522 } else {
1523 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1524 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001525 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001526 }
Selim Cinek67b22602014-03-10 15:40:16 +01001527 }
1528 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001529 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001530 mActivePointerId = INVALID_POINTER;
1531 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001532 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001533
Selim Cinek67b22602014-03-10 15:40:16 +01001534 break;
1535 case MotionEvent.ACTION_CANCEL:
1536 if (mIsBeingDragged && getChildCount() > 0) {
1537 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001538 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001539 }
1540 mActivePointerId = INVALID_POINTER;
1541 endDrag();
1542 }
1543 break;
1544 case MotionEvent.ACTION_POINTER_DOWN: {
1545 final int index = ev.getActionIndex();
1546 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001547 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001548 mActivePointerId = ev.getPointerId(index);
1549 break;
1550 }
1551 case MotionEvent.ACTION_POINTER_UP:
1552 onSecondaryPointerUp(ev);
1553 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001554 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001555 break;
1556 }
1557 return true;
1558 }
1559
Muyuan Li84b45612016-06-01 11:05:08 -07001560 protected boolean isInsideQsContainer(MotionEvent ev) {
1561 return ev.getY() < mQsContainer.getBottom();
1562 }
1563
Selim Cinek1408eb52014-06-02 14:45:38 +02001564 private void onOverScrollFling(boolean open, int initialVelocity) {
1565 if (mOverscrollTopChangedListener != null) {
1566 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1567 }
1568 mDontReportNextOverScroll = true;
1569 setOverScrollAmount(0.0f, true, false);
1570 }
1571
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001572 /**
1573 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1574 *
1575 * @param deltaY The amount to scroll upwards, has to be positive.
1576 * @return The amount of scrolling to be performed by the scroller,
1577 * not handled by the overScroll amount.
1578 */
1579 private float overScrollUp(int deltaY, int range) {
1580 deltaY = Math.max(deltaY, 0);
1581 float currentTopAmount = getCurrentOverScrollAmount(true);
1582 float newTopAmount = currentTopAmount - deltaY;
1583 if (currentTopAmount > 0) {
1584 setOverScrollAmount(newTopAmount, true /* onTop */,
1585 false /* animate */);
1586 }
1587 // Top overScroll might not grab all scrolling motion,
1588 // we have to scroll as well.
1589 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1590 float newScrollY = mOwnScrollY + scrollAmount;
1591 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001592 if (!mExpandedInThisMotion) {
1593 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1594 // We overScroll on the top
1595 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1596 false /* onTop */,
1597 false /* animate */);
1598 }
Selim Cinekef406062016-09-29 17:33:13 -07001599 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001600 scrollAmount = 0.0f;
1601 }
1602 return scrollAmount;
1603 }
1604
1605 /**
1606 * Perform a scroll downward and adapt the overscroll amounts accordingly
1607 *
1608 * @param deltaY The amount to scroll downwards, has to be negative.
1609 * @return The amount of scrolling to be performed by the scroller,
1610 * not handled by the overScroll amount.
1611 */
1612 private float overScrollDown(int deltaY) {
1613 deltaY = Math.min(deltaY, 0);
1614 float currentBottomAmount = getCurrentOverScrollAmount(false);
1615 float newBottomAmount = currentBottomAmount + deltaY;
1616 if (currentBottomAmount > 0) {
1617 setOverScrollAmount(newBottomAmount, false /* onTop */,
1618 false /* animate */);
1619 }
1620 // Bottom overScroll might not grab all scrolling motion,
1621 // we have to scroll as well.
1622 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1623 float newScrollY = mOwnScrollY + scrollAmount;
1624 if (newScrollY < 0) {
1625 float currentTopPixels = getCurrentOverScrolledPixels(true);
1626 // We overScroll on the top
1627 setOverScrolledPixels(currentTopPixels - newScrollY,
1628 true /* onTop */,
1629 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001630 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001631 scrollAmount = 0.0f;
1632 }
1633 return scrollAmount;
1634 }
1635
Selim Cinek67b22602014-03-10 15:40:16 +01001636 private void onSecondaryPointerUp(MotionEvent ev) {
1637 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1638 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1639 final int pointerId = ev.getPointerId(pointerIndex);
1640 if (pointerId == mActivePointerId) {
1641 // This was our active pointer going up. Choose a new
1642 // active pointer and adjust accordingly.
1643 // TODO: Make this decision more intelligent.
1644 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1645 mLastMotionY = (int) ev.getY(newPointerIndex);
1646 mActivePointerId = ev.getPointerId(newPointerIndex);
1647 if (mVelocityTracker != null) {
1648 mVelocityTracker.clear();
1649 }
1650 }
1651 }
1652
1653 private void initVelocityTrackerIfNotExists() {
1654 if (mVelocityTracker == null) {
1655 mVelocityTracker = VelocityTracker.obtain();
1656 }
1657 }
1658
1659 private void recycleVelocityTracker() {
1660 if (mVelocityTracker != null) {
1661 mVelocityTracker.recycle();
1662 mVelocityTracker = null;
1663 }
1664 }
1665
1666 private void initOrResetVelocityTracker() {
1667 if (mVelocityTracker == null) {
1668 mVelocityTracker = VelocityTracker.obtain();
1669 } else {
1670 mVelocityTracker.clear();
1671 }
1672 }
1673
Ricky Waicd35def2016-05-03 11:07:07 +01001674 public void setFinishScrollingCallback(Runnable runnable) {
1675 mFinishScrollingCallback = runnable;
1676 }
1677
Selim Cinek9212de82017-02-06 16:04:28 -08001678 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001679 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001680 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001681 int y = mScroller.getCurrY();
1682
Selim Cinek9212de82017-02-06 16:04:28 -08001683 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001684 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001685 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1686 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001687 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001688 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1689 }
1690 }
Selim Cinek67b22602014-03-10 15:40:16 +01001691
Adrian Roos5153d4a2016-03-22 10:01:56 -07001692 if (mDontClampNextScroll) {
1693 range = Math.max(range, oldY);
1694 }
Selim Cinek9212de82017-02-06 16:04:28 -08001695 customOverScrollBy(y - oldY, oldY, range,
1696 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001697 }
1698
Selim Cinek9212de82017-02-06 16:04:28 -08001699 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001700 } else {
1701 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001702 if (mFinishScrollingCallback != null) {
1703 mFinishScrollingCallback.run();
1704 }
Selim Cinek67b22602014-03-10 15:40:16 +01001705 }
1706 }
1707
Selim Cinek9212de82017-02-06 16:04:28 -08001708 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1709 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001710
1711 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001712 final int top = -maxOverScrollY;
1713 final int bottom = maxOverScrollY + scrollRangeY;
1714
1715 boolean clampedY = false;
1716 if (newScrollY > bottom) {
1717 newScrollY = bottom;
1718 clampedY = true;
1719 } else if (newScrollY < top) {
1720 newScrollY = top;
1721 clampedY = true;
1722 }
1723
Selim Cinek9212de82017-02-06 16:04:28 -08001724 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001725
1726 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001727 }
1728
1729 /**
1730 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1731 * overscroll effect based on numPixels. By default this will also cancel animations on the
1732 * same overScroll edge.
1733 *
1734 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1735 * the rubber-banding logic.
1736 * @param onTop Should the effect be applied on top of the scroller.
1737 * @param animate Should an animation be performed.
1738 */
1739 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001740 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001741 }
1742
1743 /**
1744 * Set the effective overScroll amount which will be directly reflected in the layout.
1745 * By default this will also cancel animations on the same overScroll edge.
1746 *
1747 * @param amount The amount to overScroll by.
1748 * @param onTop Should the effect be applied on top of the scroller.
1749 * @param animate Should an animation be performed.
1750 */
1751 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1752 setOverScrollAmount(amount, onTop, animate, true);
1753 }
1754
1755 /**
1756 * Set the effective overScroll amount which will be directly reflected in the layout.
1757 *
1758 * @param amount The amount to overScroll by.
1759 * @param onTop Should the effect be applied on top of the scroller.
1760 * @param animate Should an animation be performed.
1761 * @param cancelAnimators Should running animations be cancelled.
1762 */
1763 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1764 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001765 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1766 }
1767
1768 /**
1769 * Set the effective overScroll amount which will be directly reflected in the layout.
1770 *
1771 * @param amount The amount to overScroll by.
1772 * @param onTop Should the effect be applied on top of the scroller.
1773 * @param animate Should an animation be performed.
1774 * @param cancelAnimators Should running animations be cancelled.
1775 * @param isRubberbanded The value which will be passed to
1776 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1777 */
1778 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1779 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001780 if (cancelAnimators) {
1781 mStateAnimator.cancelOverScrollAnimators(onTop);
1782 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001783 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001784 }
1785
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001786 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1787 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001788 amount = Math.max(0, amount);
1789 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001790 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001791 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001792 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001793 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001794 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001795 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001796 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001797 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001798 }
1799 }
1800
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001801 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001802 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1803 if (mDontReportNextOverScroll) {
1804 mDontReportNextOverScroll = false;
1805 return;
1806 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001807 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001808 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001809 }
1810 }
1811
1812 public void setOverscrollTopChangedListener(
1813 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1814 mOverscrollTopChangedListener = overscrollTopChangedListener;
1815 }
1816
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001817 public float getCurrentOverScrollAmount(boolean top) {
1818 return mAmbientState.getOverScrollAmount(top);
1819 }
1820
1821 public float getCurrentOverScrolledPixels(boolean top) {
1822 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
1823 }
1824
1825 private void setOverScrolledPixels(float amount, boolean onTop) {
1826 if (onTop) {
1827 mOverScrolledTopPixels = amount;
1828 } else {
1829 mOverScrolledBottomPixels = amount;
1830 }
1831 }
1832
Selim Cinek9212de82017-02-06 16:04:28 -08001833 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001834 // Treat animating scrolls differently; see #computeScroll() for why.
1835 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07001836 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001837 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001838 springBack();
1839 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02001840 float overScrollTop = getCurrentOverScrollAmount(true);
1841 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001842 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001843 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001844 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001845 }
Selim Cinek67b22602014-03-10 15:40:16 +01001846 }
Selim Cinek67b22602014-03-10 15:40:16 +01001847 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08001848 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001849 }
1850 }
1851
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001852 private void springBack() {
1853 int scrollRange = getScrollRange();
1854 boolean overScrolledTop = mOwnScrollY <= 0;
1855 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
1856 if (overScrolledTop || overScrolledBottom) {
1857 boolean onTop;
1858 float newAmount;
1859 if (overScrolledTop) {
1860 onTop = true;
1861 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07001862 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02001863 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001864 } else {
1865 onTop = false;
1866 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07001867 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001868 }
1869 setOverScrollAmount(newAmount, onTop, false);
1870 setOverScrollAmount(0.0f, onTop, true);
1871 mScroller.forceFinished(true);
1872 }
1873 }
1874
Selim Cinek67b22602014-03-10 15:40:16 +01001875 private int getScrollRange() {
Selim Cineka424c502016-04-05 13:07:54 -07001876 int contentHeight = getContentHeight();
Selim Cinekdb167372016-11-17 15:41:17 -08001877 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07001878 int imeInset = getImeInset();
1879 scrollRange += Math.min(imeInset, Math.max(0,
1880 getContentHeight() - (getHeight() - imeInset)));
1881 return scrollRange;
1882 }
1883
1884 private int getImeInset() {
1885 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01001886 }
1887
Selim Cinek343e6e22014-04-11 21:23:30 +02001888 /**
1889 * @return the first child which has visibility unequal to GONE
1890 */
Selim Cinekb55386d2015-12-16 17:26:49 -08001891 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02001892 int childCount = getChildCount();
1893 for (int i = 0; i < childCount; i++) {
1894 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001895 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08001896 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02001897 }
1898 }
1899 return null;
1900 }
1901
Selim Cinek4a1ac842014-05-01 15:51:58 +02001902 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001903 * @return the child before the given view which has visibility unequal to GONE
1904 */
1905 public ExpandableView getViewBeforeView(ExpandableView view) {
1906 ExpandableView previousView = null;
1907 int childCount = getChildCount();
1908 for (int i = 0; i < childCount; i++) {
1909 View child = getChildAt(i);
1910 if (child == view) {
1911 return previousView;
1912 }
1913 if (child.getVisibility() != View.GONE) {
1914 previousView = (ExpandableView) child;
1915 }
1916 }
1917 return null;
1918 }
1919
1920 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001921 * @return The first child which has visibility unequal to GONE which is currently below the
1922 * given translationY or equal to it.
1923 */
Selim Cinekef8c2252017-02-10 14:52:18 -08001924 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001925 int childCount = getChildCount();
1926 for (int i = 0; i < childCount; i++) {
1927 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08001928 if (child.getVisibility() == View.GONE) {
1929 continue;
1930 }
1931 float rowTranslation = child.getTranslationY();
1932 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001933 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08001934 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
1935 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1936 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
1937 List<ExpandableNotificationRow> notificationChildren =
1938 row.getNotificationChildren();
1939 for (int childIndex = 0; childIndex < notificationChildren.size();
1940 childIndex++) {
1941 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
1942 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
1943 return rowChild;
1944 }
1945 }
1946 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001947 }
1948 }
1949 return null;
1950 }
1951
1952 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02001953 * @return the last child which has visibility unequal to GONE
1954 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001955 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02001956 int childCount = getChildCount();
1957 for (int i = childCount - 1; i >= 0; i--) {
1958 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001959 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02001960 return child;
1961 }
1962 }
1963 return null;
1964 }
1965
Jorim Jaggi069cd032014-05-15 03:09:01 +02001966 /**
1967 * @return the number of children which have visibility unequal to GONE
1968 */
1969 public int getNotGoneChildCount() {
1970 int childCount = getChildCount();
1971 int count = 0;
1972 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07001973 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001974 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02001975 count++;
1976 }
1977 }
1978 return count;
1979 }
1980
Jorim Jaggi30c305c2014-07-01 23:34:41 +02001981 public int getContentHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001982 return mContentHeight;
1983 }
1984
1985 private void updateContentHeight() {
1986 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08001987 float previousPaddingRequest = mPaddingBetweenElements;
1988 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07001989 int numShownItems = 0;
1990 boolean finish = false;
Adrian Roos7a9551a2017-01-11 12:27:49 -08001991 int maxDisplayedNotifications = mAmbientState.isDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07001992 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08001993 : mMaxDisplayedNotifications;
1994
Selim Cinek67b22602014-03-10 15:40:16 +01001995 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08001996 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Selim Cinek281c2022016-10-13 19:14:43 -07001997 if (expandableView.getVisibility() != View.GONE
1998 && !expandableView.hasNoContentHeight()) {
Adrian Roos7d062c42017-03-30 15:11:43 -07001999 boolean limitReached = maxDisplayedNotifications != -1
2000 && numShownItems >= maxDisplayedNotifications;
Selim Cinekbe2c4432017-05-30 12:11:09 -07002001 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isDark()
2002 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07002003 && expandableView instanceof ExpandableNotificationRow
2004 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
2005 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002006 expandableView = mShelf;
2007 finish = true;
2008 }
Selim Cinek42357e02016-02-24 18:48:01 -08002009 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002010 float padding;
2011 if (increasedPaddingAmount >= 0.0f) {
2012 padding = (int) NotificationUtils.interpolate(
2013 previousPaddingRequest,
2014 mIncreasedPaddingBetweenElements,
2015 increasedPaddingAmount);
2016 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002017 mPaddingBetweenElements,
2018 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002019 increasedPaddingAmount);
2020 } else {
2021 int ownPadding = (int) NotificationUtils.interpolate(
2022 0,
2023 mPaddingBetweenElements,
2024 1.0f + increasedPaddingAmount);
2025 if (previousPaddingAmount > 0.0f) {
2026 padding = (int) NotificationUtils.interpolate(
2027 ownPadding,
2028 mIncreasedPaddingBetweenElements,
2029 previousPaddingAmount);
2030 } else {
2031 padding = ownPadding;
2032 }
2033 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002034 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002035 if (height != 0) {
2036 height += padding;
2037 }
2038 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002039 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002040 numShownItems++;
2041 if (finish) {
2042 break;
2043 }
Selim Cinek67b22602014-03-10 15:40:16 +01002044 }
2045 }
Anthony Chen9fe1ee72017-04-07 13:53:37 -07002046 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002047 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002048 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002049 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002050 }
2051
Adrian Roos7d062c42017-03-30 15:11:43 -07002052 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002053 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002054 }
2055
Selim Cinekbe2c4432017-05-30 12:11:09 -07002056 public boolean hasPulsingNotifications() {
Adrian Roos7d062c42017-03-30 15:11:43 -07002057 return mPulsing != null;
2058 }
2059
Selim Cinekc22fff62016-05-20 12:44:30 -07002060 private void updateScrollability() {
2061 boolean scrollable = getScrollRange() > 0;
2062 if (scrollable != mScrollable) {
2063 mScrollable = scrollable;
2064 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002065 updateForwardAndBackwardScrollability();
2066 }
2067 }
2068
2069 private void updateForwardAndBackwardScrollability() {
2070 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2071 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2072 boolean changed = forwardScrollable != mForwardScrollable
2073 || backwardsScrollable != mBackwardScrollable;
2074 mForwardScrollable = forwardScrollable;
2075 mBackwardScrollable = backwardsScrollable;
2076 if (changed) {
2077 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002078 }
Selim Cinek67b22602014-03-10 15:40:16 +01002079 }
2080
Selim Cinek6811d722016-01-19 17:53:12 -08002081 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002082 // No need to update the background color if it's not being drawn.
2083 if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002084 return;
2085 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002086
Selim Cinek6811d722016-01-19 17:53:12 -08002087 updateBackgroundBounds();
2088 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002089 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2090 || areBoundsAnimating();
2091 if (!isExpanded()) {
2092 abortBackgroundAnimators();
2093 animate = false;
2094 }
2095 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002096 startBackgroundAnimation();
2097 } else {
2098 mCurrentBounds.set(mBackgroundBounds);
2099 applyCurrentBackgroundBounds();
2100 }
2101 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002102 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002103 }
Selim Cinek614576e2016-01-20 10:54:09 -08002104 mAnimateNextBackgroundBottom = false;
2105 mAnimateNextBackgroundTop = false;
2106 }
2107
Selim Cinek54680902016-10-19 16:49:44 -07002108 private void abortBackgroundAnimators() {
2109 if (mBottomAnimator != null) {
2110 mBottomAnimator.cancel();
2111 }
2112 if (mTopAnimator != null) {
2113 mTopAnimator.cancel();
2114 }
2115 }
2116
Selim Cinek614576e2016-01-20 10:54:09 -08002117 private boolean areBoundsAnimating() {
2118 return mBottomAnimator != null || mTopAnimator != null;
2119 }
2120
2121 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002122 // left and right are always instantly applied
2123 mCurrentBounds.left = mBackgroundBounds.left;
2124 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002125 startBottomAnimation();
2126 startTopAnimation();
2127 }
2128
2129 private void startTopAnimation() {
2130 int previousEndValue = mEndAnimationRect.top;
2131 int newEndValue = mBackgroundBounds.top;
2132 ObjectAnimator previousAnimator = mTopAnimator;
2133 if (previousAnimator != null && previousEndValue == newEndValue) {
2134 return;
2135 }
2136 if (!mAnimateNextBackgroundTop) {
2137 // just a local update was performed
2138 if (previousAnimator != null) {
2139 // we need to increase all animation keyframes of the previous animator by the
2140 // relative change to the end value
2141 int previousStartValue = mStartAnimationRect.top;
2142 PropertyValuesHolder[] values = previousAnimator.getValues();
2143 values[0].setIntValues(previousStartValue, newEndValue);
2144 mStartAnimationRect.top = previousStartValue;
2145 mEndAnimationRect.top = newEndValue;
2146 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2147 return;
2148 } else {
2149 // no new animation needed, let's just apply the value
2150 setBackgroundTop(newEndValue);
2151 return;
2152 }
2153 }
2154 if (previousAnimator != null) {
2155 previousAnimator.cancel();
2156 }
2157 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2158 mCurrentBounds.top, newEndValue);
2159 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2160 animator.setInterpolator(interpolator);
2161 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2162 // remove the tag when the animation is finished
2163 animator.addListener(new AnimatorListenerAdapter() {
2164 @Override
2165 public void onAnimationEnd(Animator animation) {
2166 mStartAnimationRect.top = -1;
2167 mEndAnimationRect.top = -1;
2168 mTopAnimator = null;
2169 }
2170 });
2171 animator.start();
2172 mStartAnimationRect.top = mCurrentBounds.top;
2173 mEndAnimationRect.top = newEndValue;
2174 mTopAnimator = animator;
2175 }
2176
2177 private void startBottomAnimation() {
2178 int previousStartValue = mStartAnimationRect.bottom;
2179 int previousEndValue = mEndAnimationRect.bottom;
2180 int newEndValue = mBackgroundBounds.bottom;
2181 ObjectAnimator previousAnimator = mBottomAnimator;
2182 if (previousAnimator != null && previousEndValue == newEndValue) {
2183 return;
2184 }
2185 if (!mAnimateNextBackgroundBottom) {
2186 // just a local update was performed
2187 if (previousAnimator != null) {
2188 // we need to increase all animation keyframes of the previous animator by the
2189 // relative change to the end value
2190 PropertyValuesHolder[] values = previousAnimator.getValues();
2191 values[0].setIntValues(previousStartValue, newEndValue);
2192 mStartAnimationRect.bottom = previousStartValue;
2193 mEndAnimationRect.bottom = newEndValue;
2194 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2195 return;
2196 } else {
2197 // no new animation needed, let's just apply the value
2198 setBackgroundBottom(newEndValue);
2199 return;
2200 }
2201 }
2202 if (previousAnimator != null) {
2203 previousAnimator.cancel();
2204 }
2205 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2206 mCurrentBounds.bottom, newEndValue);
2207 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2208 animator.setInterpolator(interpolator);
2209 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2210 // remove the tag when the animation is finished
2211 animator.addListener(new AnimatorListenerAdapter() {
2212 @Override
2213 public void onAnimationEnd(Animator animation) {
2214 mStartAnimationRect.bottom = -1;
2215 mEndAnimationRect.bottom = -1;
2216 mBottomAnimator = null;
2217 }
2218 });
2219 animator.start();
2220 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2221 mEndAnimationRect.bottom = newEndValue;
2222 mBottomAnimator = animator;
2223 }
2224
2225 private void setBackgroundTop(int top) {
2226 mCurrentBounds.top = top;
2227 applyCurrentBackgroundBounds();
2228 }
2229
2230 public void setBackgroundBottom(int bottom) {
2231 mCurrentBounds.bottom = bottom;
2232 applyCurrentBackgroundBounds();
2233 }
2234
2235 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002236 // If the background of the notification is not being drawn, then there is no need to
2237 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2238 if (!mShouldDrawNotificationBackground) {
2239 return;
2240 }
2241
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002242 mScrimController.setExcludedBackgroundArea(
Selim Cinek48ff9b42016-11-09 19:31:51 -08002243 mFadingOut || mParentNotFullyVisible || mAmbientState.isDark() || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002244 : mCurrentBounds);
Selim Cinek515b2032017-11-15 10:20:19 -08002245 updateBackgroundPath();
Selim Cinek614576e2016-01-20 10:54:09 -08002246 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002247 }
2248
Selim Cinek515b2032017-11-15 10:20:19 -08002249 private void updateBackgroundPath() {
2250 mBackgroundPath.reset();
2251 float topRoundness = 0;
2252 if (mFirstVisibleBackgroundChild != null) {
2253 topRoundness = mFirstVisibleBackgroundChild.getCurrentBackgroundRadiusTop();
2254 }
2255 topRoundness = onKeyguard() ? mCornerRadius : topRoundness;
2256 float bottomRoundNess = mCornerRadius;
2257 mBackgroundRadii[0] = topRoundness;
2258 mBackgroundRadii[1] = topRoundness;
2259 mBackgroundRadii[2] = topRoundness;
2260 mBackgroundRadii[3] = topRoundness;
2261 mBackgroundRadii[4] = bottomRoundNess;
2262 mBackgroundRadii[5] = bottomRoundNess;
2263 mBackgroundRadii[6] = bottomRoundNess;
2264 mBackgroundRadii[7] = bottomRoundNess;
2265 mBackgroundPath.addRoundRect(mCurrentSidePadding, mCurrentBounds.top,
2266 getWidth() - mCurrentSidePadding, mCurrentBounds.bottom, mBackgroundRadii,
2267 Path.Direction.CCW);
2268 }
2269
Selim Cinek6811d722016-01-19 17:53:12 -08002270 /**
2271 * Update the background bounds to the new desired bounds
2272 */
2273 private void updateBackgroundBounds() {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01002274 if (mAmbientState.isPanelFullWidth()) {
2275 mBackgroundBounds.left = 0;
2276 mBackgroundBounds.right = getWidth();
2277 } else {
2278 getLocationInWindow(mTempInt2);
2279 mBackgroundBounds.left = mTempInt2[0];
2280 mBackgroundBounds.right = mTempInt2[0] + getWidth();
2281 }
Selim Cinek515b2032017-11-15 10:20:19 -08002282 mBackgroundBounds.left += mCurrentSidePadding;
2283 mBackgroundBounds.right -= mCurrentSidePadding;
Selim Cinek614576e2016-01-20 10:54:09 -08002284 if (!mIsExpanded) {
2285 mBackgroundBounds.top = 0;
2286 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002287 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002288 }
2289 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002290 int top = 0;
2291 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002292 // Round Y up to avoid seeing the background during animation
2293 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002294 if (mAnimateNextBackgroundTop
2295 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2296 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002297 // we're ending up at the same location as we are now, lets just skip the animation
2298 top = finalTranslationY;
2299 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002300 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002301 }
2302 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002303 ActivatableNotificationView lastView =
2304 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2305 ? mShelf
2306 : mLastVisibleBackgroundChild;
2307 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002308 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002309 int finalTranslationY;
2310 if (lastView == mShelf) {
2311 finalTranslationY = (int) mShelf.getTranslationY();
2312 } else {
2313 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2314 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002315 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002316 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek6811d722016-01-19 17:53:12 -08002317 finalBottom = Math.min(finalBottom, getHeight());
Selim Cinek614576e2016-01-20 10:54:09 -08002318 if (mAnimateNextBackgroundBottom
2319 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2320 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002321 // we're ending up at the same location as we are now, lets just skip the animation
2322 bottom = finalBottom;
2323 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002324 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2325 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002326 bottom = Math.min(bottom, getHeight());
2327 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002328 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002329 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002330 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002331 }
Selim Cinek355652a2016-12-07 13:32:12 -08002332 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002333 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002334 } else {
2335 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002336 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002337 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002338 mBackgroundBounds.top = top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002339 mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
Selim Cinek6811d722016-01-19 17:53:12 -08002340 }
2341
Selim Cinek614576e2016-01-20 10:54:09 -08002342 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2343 int childCount = getChildCount();
2344 for (int i = 0; i < childCount; i++) {
2345 View child = getChildAt(i);
2346 if (child.getVisibility() != View.GONE
2347 && child instanceof ExpandableNotificationRow) {
2348 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2349 if (row.isPinned()) {
2350 return row;
2351 }
2352 }
2353 }
2354 return null;
2355 }
2356
2357 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002358 int childCount = getChildCount();
2359 for (int i = childCount - 1; i >= 0; i--) {
2360 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002361 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2362 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002363 return (ActivatableNotificationView) child;
2364 }
2365 }
2366 return null;
2367 }
2368
Selim Cinek614576e2016-01-20 10:54:09 -08002369 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002370 int childCount = getChildCount();
2371 for (int i = 0; i < childCount; i++) {
2372 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002373 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2374 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002375 return (ActivatableNotificationView) child;
2376 }
2377 }
2378 return null;
2379 }
2380
Selim Cinek67b22602014-03-10 15:40:16 +01002381 /**
2382 * Fling the scroll view
2383 *
2384 * @param velocityY The initial velocity in the Y direction. Positive
2385 * numbers mean that the finger/cursor is moving down the screen,
2386 * which means we want to scroll towards the top.
2387 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002388 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002389 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002390 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002391
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002392 float topAmount = getCurrentOverScrollAmount(true);
2393 float bottomAmount = getCurrentOverScrollAmount(false);
2394 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002395 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002396 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002397 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002398 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002399 * mOverflingDistance + topAmount;
2400 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002401 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002402 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002403 mMaxOverScroll = Math.abs(velocityY) / 1000f
2404 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2405 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002406 } else {
2407 // it will be set once we reach the boundary
2408 mMaxOverScroll = 0.0f;
2409 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002410 int minScrollY = Math.max(0, scrollRange);
2411 if (mExpandedInThisMotion) {
2412 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2413 }
Selim Cinek9212de82017-02-06 16:04:28 -08002414 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2415 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002416
Selim Cinek9212de82017-02-06 16:04:28 -08002417 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002418 }
2419 }
2420
Selim Cinek1408eb52014-06-02 14:45:38 +02002421 /**
2422 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2423 * overScroll view (i.e QS).
2424 */
2425 private boolean shouldOverScrollFling(int initialVelocity) {
2426 float topOverScroll = getCurrentOverScrollAmount(true);
2427 return mScrolledToTopOnFirstDown
2428 && !mExpandedInThisMotion
2429 && topOverScroll > mMinTopOverScrollToEscape
2430 && initialVelocity > 0;
2431 }
2432
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002433 /**
2434 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2435 * account.
2436 *
2437 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002438 * @param animate whether to animate the change
2439 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2440 * {@code qsHeight} is the final top padding
2441 */
Jason Monk16ac3772016-02-10 15:39:21 -05002442 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002443 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002444 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002445 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002446 if (topPadding + minStackHeight > getHeight()) {
2447 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002448 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002449 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002450 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002451 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002452 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002453 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002454 }
2455
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002456 public int getLayoutMinHeight() {
Anthony Chen9e05d462017-04-07 10:10:21 -07002457 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002458 }
2459
Selim Cineka981d082016-08-03 14:45:31 -07002460 public int getFirstChildIntrinsicHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002461 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002462 int firstChildMinHeight = firstChild != null
2463 ? firstChild.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002464 : mEmptyShadeView != null
Selim Cineka981d082016-08-03 14:45:31 -07002465 ? mEmptyShadeView.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002466 : mCollapsedSize;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002467 if (mOwnScrollY > 0) {
2468 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2469 }
Selim Cinek94c2d822016-07-13 18:50:04 -07002470 return firstChildMinHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002471 }
2472
2473 public float getTopPaddingOverflow() {
2474 return mTopPaddingOverflow;
2475 }
2476
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002477 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002478 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002479 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002480 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002481 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002482 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002483 shelfHeight = mShelf.getIntrinsicHeight();
2484 }
2485 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002486 }
2487
Selim Cinek1408eb52014-06-02 14:45:38 +02002488 private int clampPadding(int desiredPadding) {
2489 return Math.max(desiredPadding, mIntrinsicPadding);
2490 }
2491
Selim Cinekfed1ab62014-06-17 14:10:33 -07002492 private float getRubberBandFactor(boolean onTop) {
2493 if (!onTop) {
2494 return RUBBER_BAND_FACTOR_NORMAL;
2495 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002496 if (mExpandedInThisMotion) {
2497 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002498 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002499 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2500 } else if (mScrolledToTopOnFirstDown) {
2501 return 1.0f;
2502 }
2503 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002504 }
2505
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002506 /**
2507 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2508 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2509 * overscroll view (e.g. expand QS).
2510 */
2511 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002512 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002513 || !mScrolledToTopOnFirstDown;
2514 }
2515
Selim Cinek67b22602014-03-10 15:40:16 +01002516 private void endDrag() {
2517 setIsBeingDragged(false);
2518
2519 recycleVelocityTracker();
2520
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002521 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2522 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2523 }
2524 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2525 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2526 }
Selim Cinek67b22602014-03-10 15:40:16 +01002527 }
2528
Jorim Jaggi56306252014-07-03 00:40:09 +02002529 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2530 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2531 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2532 }
2533
Selim Cinek67b22602014-03-10 15:40:16 +01002534 @Override
2535 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002536 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002537 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002538 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002539 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002540 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2541 }
Selim Cinek67b22602014-03-10 15:40:16 +01002542 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002543 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002544 scrollWantsIt = onInterceptTouchEventScroll(ev);
2545 }
2546 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002547 if (!mIsBeingDragged
2548 && !mExpandingNotification
2549 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002550 && !mOnlyScrollingInThisMotion
2551 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002552 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2553 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002554 // Check if we need to clear any snooze leavebehinds
2555 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002556 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07002557 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2558 && !scrollWantsIt) {
2559 mCheckForLeavebehind = false;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002560 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
2561 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2562 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07002563 }
2564 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2565 mCheckForLeavebehind = true;
2566 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002567 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2568 }
2569
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002570 private void handleEmptySpaceClick(MotionEvent ev) {
2571 switch (ev.getActionMasked()) {
2572 case MotionEvent.ACTION_MOVE:
2573 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2574 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2575 mTouchIsClick = false;
2576 }
2577 break;
2578 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002579 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002580 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2581 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2582 }
2583 break;
2584 }
2585 }
2586
Selim Cinek1408eb52014-06-02 14:45:38 +02002587 private void initDownStates(MotionEvent ev) {
2588 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2589 mExpandedInThisMotion = false;
2590 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002591 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002592 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002593 mTouchIsClick = true;
2594 mInitialTouchX = ev.getX();
2595 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002596 }
Selim Cinek67b22602014-03-10 15:40:16 +01002597 }
2598
Selim Cinekef5127e2015-12-21 16:55:58 -08002599 public void setChildTransferInProgress(boolean childTransferInProgress) {
2600 mChildTransferInProgress = childTransferInProgress;
2601 }
2602
Christoph Studer068f5922014-04-08 17:43:07 -04002603 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002604 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002605 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002606 // we only call our internal methods if this is actually a removal and not just a
2607 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002608 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002609 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002610 }
2611 }
2612
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002613 /**
2614 * Called when a notification is removed from the shade. This cleans up the state for a given
2615 * view.
2616 */
2617 public void cleanUpViewState(View child) {
2618 if (child == mTranslatingParentView) {
2619 mTranslatingParentView = null;
2620 }
2621 mCurrentStackScrollState.removeViewStateForView(child);
2622 }
2623
Selim Cinek197823d2016-03-24 13:06:00 -07002624 @Override
2625 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2626 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2627 if (disallowIntercept) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002628 cancelLongPress();
Selim Cinek197823d2016-03-24 13:06:00 -07002629 }
2630 }
2631
Selim Cineka5703182016-05-11 21:23:16 -04002632 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002633 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002634 // This is only a position change, don't do anything special
2635 return;
2636 }
Selim Cinekd1395642016-04-28 12:22:42 -07002637 ExpandableView expandableView = (ExpandableView) child;
2638 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002639 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002640 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002641 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002642 if (animationGenerated) {
2643 if (!mSwipedOutViews.contains(child)) {
Selim Cineka5703182016-05-11 21:23:16 -04002644 container.getOverlay().add(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002645 } else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002646 container.addTransientView(child, 0);
2647 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002648 }
2649 } else {
2650 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002651 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002652 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002653
Selim Cineke9bad242016-06-15 11:46:37 -07002654 focusNextViewIfFocused(child);
2655 }
2656
2657 private void focusNextViewIfFocused(View view) {
2658 if (view instanceof ExpandableNotificationRow) {
2659 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2660 if (row.shouldRefocusOnDismiss()) {
2661 View nextView = row.getChildAfterViewWhenDismissed();
2662 if (nextView == null) {
2663 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2664 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2665 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002666 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002667 }
2668 if (nextView != null) {
2669 nextView.requestAccessibilityFocus();
2670 }
2671 }
2672 }
2673
Selim Cinekc27437b2014-05-14 10:23:33 +02002674 }
2675
Selim Cinekb5605e52015-02-20 18:21:41 +01002676 private boolean isChildInGroup(View child) {
2677 return child instanceof ExpandableNotificationRow
2678 && mGroupManager.isChildInGroupWithSummary(
2679 ((ExpandableNotificationRow) child).getStatusBarNotification());
2680 }
2681
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002682 /**
2683 * Generate a remove animation for a child view.
2684 *
2685 * @param child The view to generate the remove animation for.
2686 * @return Whether an animation was generated.
2687 */
2688 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002689 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002690 mAddedHeadsUpChildren.remove(child);
2691 return false;
2692 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002693 if (isClickedHeadsUp(child)) {
2694 // An animation is already running, add it to the Overlay
2695 mClearOverlayViewsWhenFinished.add(child);
2696 return true;
2697 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002698 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002699 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002700 // Generate Animations
2701 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002702 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002703 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002704 } else {
2705 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002706 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002707 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002708 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002709 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002710 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002711 }
2712
Selim Cinek0fccc722015-07-29 17:04:36 -07002713 private boolean isClickedHeadsUp(View child) {
2714 return HeadsUpManager.isClickedHeadsUpNotification(child);
2715 }
2716
Selim Cineke0890e52015-06-17 11:17:08 -07002717 /**
2718 * Remove a removed child view from the heads up animations if it was just added there
2719 *
2720 * @return whether any child was removed from the list to animate
2721 */
2722 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2723 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002724 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2725 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002726 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002727 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002728 mTmpList.add(eventPair);
2729 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002730 }
2731 }
Selim Cineke0890e52015-06-17 11:17:08 -07002732 if (hasAddEvent) {
2733 // This child was just added lets remove all events.
2734 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002735 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002736 }
2737 mTmpList.clear();
2738 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002739 }
2740
Selim Cinek572bbd42014-04-25 16:43:27 +02002741 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002742 * @param child the child to query
2743 * @return whether a view is not a top level child but a child notification and that group is
2744 * not expanded
2745 */
2746 private boolean isChildInInvisibleGroup(View child) {
2747 if (child instanceof ExpandableNotificationRow) {
2748 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2749 ExpandableNotificationRow groupSummary =
2750 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2751 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002752 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002753 }
2754 }
2755 return false;
2756 }
2757
2758 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002759 * Updates the scroll position when a child was removed
2760 *
2761 * @param removedChild the removed child
2762 */
Selim Cinek61633a82016-01-25 15:54:10 -08002763 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002764 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002765 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2766 int padding;
2767 if (increasedPaddingAmount >= 0) {
2768 padding = (int) NotificationUtils.interpolate(
2769 mPaddingBetweenElements,
2770 mIncreasedPaddingBetweenElements,
2771 increasedPaddingAmount);
2772 } else {
2773 padding = (int) NotificationUtils.interpolate(
2774 0,
2775 mPaddingBetweenElements,
2776 1.0f + increasedPaddingAmount);
2777 }
Selim Cinek61633a82016-01-25 15:54:10 -08002778 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002779 int endPosition = startingPosition + childHeight;
2780 if (endPosition <= mOwnScrollY) {
2781 // This child is fully scrolled of the top, so we have to deduct its height from the
2782 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002783 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002784 } else if (startingPosition < mOwnScrollY) {
2785 // This child is currently being scrolled into, set the scroll position to the start of
2786 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002787 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002788 }
2789 }
2790
Selim Cinekd7c4e002014-07-04 18:36:42 +02002791 private int getIntrinsicHeight(View view) {
2792 if (view instanceof ExpandableView) {
2793 ExpandableView expandableView = (ExpandableView) view;
2794 return expandableView.getIntrinsicHeight();
2795 }
2796 return view.getHeight();
2797 }
2798
Selim Cinek1f624952017-06-08 19:11:50 -07002799 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002800 ExpandableNotificationRow childInGroup = null;
2801 ExpandableNotificationRow requestedRow = null;
2802 if (isChildInGroup(requestedView)) {
2803 // We're asking for a child in a group. Calculate the position of the parent first,
2804 // then within the parent.
2805 childInGroup = (ExpandableNotificationRow) requestedView;
2806 requestedView = requestedRow = childInGroup.getNotificationParent();
2807 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002808 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002809 float previousPaddingRequest = mPaddingBetweenElements;
2810 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002811 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002812 ExpandableView child = (ExpandableView) getChildAt(i);
2813 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002814 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002815 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002816 float padding;
2817 if (increasedPaddingAmount >= 0.0f) {
2818 padding = (int) NotificationUtils.interpolate(
2819 previousPaddingRequest,
2820 mIncreasedPaddingBetweenElements,
2821 increasedPaddingAmount);
2822 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002823 mPaddingBetweenElements,
2824 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002825 increasedPaddingAmount);
2826 } else {
2827 int ownPadding = (int) NotificationUtils.interpolate(
2828 0,
2829 mPaddingBetweenElements,
2830 1.0f + increasedPaddingAmount);
2831 if (previousPaddingAmount > 0.0f) {
2832 padding = (int) NotificationUtils.interpolate(
2833 ownPadding,
2834 mIncreasedPaddingBetweenElements,
2835 previousPaddingAmount);
2836 } else {
2837 padding = ownPadding;
2838 }
2839 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08002840 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002841 if (position != 0) {
2842 position += padding;
2843 }
2844 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002845 }
Adrian Roos4a579672016-05-24 16:54:37 -07002846 if (child == requestedView) {
2847 if (requestedRow != null) {
2848 position += requestedRow.getPositionOfChild(childInGroup);
2849 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002850 return position;
2851 }
Selim Cinek61633a82016-01-25 15:54:10 -08002852 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02002853 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002854 }
2855 }
2856 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02002857 }
2858
2859 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002860 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02002861 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002862 onViewAddedInternal(child);
2863 }
2864
Selim Cinek614576e2016-01-20 10:54:09 -08002865 private void updateFirstAndLastBackgroundViews() {
2866 ActivatableNotificationView firstChild = getFirstChildWithBackground();
2867 ActivatableNotificationView lastChild = getLastChildWithBackground();
Selim Cinek0fe07392017-11-09 13:26:34 -08002868 boolean firstChanged = firstChild != mFirstVisibleBackgroundChild;
2869 boolean lastChanged = lastChild != mLastVisibleBackgroundChild;
Selim Cinek614576e2016-01-20 10:54:09 -08002870 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek0fe07392017-11-09 13:26:34 -08002871 mAnimateNextBackgroundTop = firstChanged;
2872 mAnimateNextBackgroundBottom = lastChanged;
Selim Cinek614576e2016-01-20 10:54:09 -08002873 } else {
2874 mAnimateNextBackgroundTop = false;
2875 mAnimateNextBackgroundBottom = false;
2876 }
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002877 if (firstChanged && mFirstVisibleBackgroundChild != null
2878 && !mFirstVisibleBackgroundChild.isRemoved()) {
2879 mFirstVisibleBackgroundChild.setTopRoundness(0.0f,
2880 mFirstVisibleBackgroundChild.isShown());
Selim Cinek0fe07392017-11-09 13:26:34 -08002881 }
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002882 if (lastChanged && mLastVisibleBackgroundChild != null
2883 && !mLastVisibleBackgroundChild.isRemoved()) {
Selim Cinek2871bef2017-11-22 08:40:00 -08002884 mLastVisibleBackgroundChild.setBottomRoundness(0.0f,
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002885 mLastVisibleBackgroundChild.isShown());
Selim Cinek0fe07392017-11-09 13:26:34 -08002886 }
Selim Cinek614576e2016-01-20 10:54:09 -08002887 mFirstVisibleBackgroundChild = firstChild;
2888 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08002889 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek515b2032017-11-15 10:20:19 -08002890 applyRoundedNess();
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002891 }
2892
2893 private void applyRoundedNess() {
Selim Cinek0fe07392017-11-09 13:26:34 -08002894 if (mFirstVisibleBackgroundChild != null) {
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002895 mFirstVisibleBackgroundChild.setTopRoundness(
2896 mStatusBarState == StatusBarState.KEYGUARD ? 1.0f : 0.0f,
2897 mFirstVisibleBackgroundChild.isShown()
2898 && !mChildrenToAddAnimated.contains(mFirstVisibleBackgroundChild));
Selim Cinek0fe07392017-11-09 13:26:34 -08002899 }
2900 if (mLastVisibleBackgroundChild != null) {
Selim Cinek2871bef2017-11-22 08:40:00 -08002901 mLastVisibleBackgroundChild.setBottomRoundness(1.0f,
Selim Cinekd9b7dd42017-11-10 17:53:47 -08002902 mLastVisibleBackgroundChild.isShown()
2903 && !mChildrenToAddAnimated.contains(mLastVisibleBackgroundChild));
Selim Cinek0fe07392017-11-09 13:26:34 -08002904 }
Selim Cinek515b2032017-11-15 10:20:19 -08002905 updateBackgroundPath();
2906 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002907 }
2908
Selim Cinekb5605e52015-02-20 18:21:41 +01002909 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002910 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02002911 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00002912 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002913 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02002914 updateChronometerForChild(child);
Selim Cinek515b2032017-11-15 10:20:19 -08002915 updateCurrentSidePaddings(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002916 }
2917
Selim Cinekd06c41c2015-07-06 14:51:36 -07002918 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002919 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002920 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002921 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07002922 }
2923 }
2924
Selim Cinekd1395642016-04-28 12:22:42 -07002925 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
2926 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01002927 }
2928
2929 public void notifyGroupChildAdded(View row) {
2930 onViewAddedInternal(row);
2931 }
2932
Jorim Jaggi75c95042014-05-16 19:09:59 +02002933 public void setAnimationsEnabled(boolean animationsEnabled) {
2934 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02002935 updateNotificationAnimationStates();
2936 }
2937
2938 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002939 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08002940 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02002941 int childCount = getChildCount();
2942 for (int i = 0; i < childCount; i++) {
2943 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07002944 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02002945 updateAnimationState(running, child);
2946 }
2947 }
2948
Selim Cinek51ae05d2014-09-09 15:51:38 +02002949 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002950 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08002951 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002952 }
2953
2954
Selim Cinekcab4a602014-09-03 14:47:57 +02002955 private void updateAnimationState(boolean running, View child) {
2956 if (child instanceof ExpandableNotificationRow) {
2957 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2958 row.setIconAnimationRunning(running);
2959 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02002960 }
2961
2962 public boolean isAddOrRemoveAnimationPending() {
2963 return mNeedsAnimation
2964 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
2965 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002966 /**
2967 * Generate an animation for an added child view.
2968 *
2969 * @param child The view to be added.
Jorim Jaggif6411742014-08-05 17:10:43 +00002970 * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002971 */
Jorim Jaggif6411742014-08-05 17:10:43 +00002972 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002973 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002974 // Generate Animations
2975 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00002976 if (fromMoreCard) {
2977 mFromMoreCardAdditions.add(child);
2978 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002979 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02002980 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08002981 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07002982 mAddedHeadsUpChildren.add(child);
2983 mChildrenToAddAnimated.remove(child);
2984 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002985 }
2986
2987 /**
2988 * Change the position of child to a new location
2989 *
2990 * @param child the view to change the position for
2991 * @param newIndex the new index
2992 */
2993 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04002994 int currentIndex = indexOfChild(child);
2995 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002996 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08002997 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02002998 removeView(child);
2999 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08003000 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003001 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003002 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003003 mChildrenChangingPositions.add(child);
3004 mNeedsAnimation = true;
3005 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003006 }
3007 }
3008
Selim Cinekf4c19962014-05-01 21:55:31 +02003009 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003010 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003011 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003012 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003013 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003014 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003015 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003016 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
3017 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003018 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003019 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003020 updateViewShadows();
Selim Cinekf4c19962014-05-01 21:55:31 +02003021 } else {
3022 applyCurrentState();
3023 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003024 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003025 }
3026
3027 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003028 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003029 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003030 generateChildAdditionEvents();
3031 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02003032 generateSnapBackEvents();
3033 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003034 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003035 generateActivateEvent();
3036 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003037 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003038 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003039 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003040 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003041 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003042 generateAnimateEverythingEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003043 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003044 }
3045
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003046 private void generateHeadsUpAnimationEvents() {
3047 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003048 ExpandableNotificationRow row = eventPair.first;
3049 boolean isHeadsUp = eventPair.second;
3050 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3051 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003052 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003053 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003054 type = row.wasJustClicked()
3055 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3056 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003057 if (row.isChildInGroup()) {
3058 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003059 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003060 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003061 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08003062 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04003063 if (viewState == null) {
3064 // A view state was never generated for this view, so we don't need to animate
3065 // this. This may happen with notification children.
3066 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003067 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003068 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3069 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3070 // Our custom add animation
3071 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3072 } else {
3073 // Normal add animation
3074 type = AnimationEvent.ANIMATION_TYPE_ADD;
3075 }
3076 onBottom = !pinnedAndClosed;
3077 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003078 }
3079 AnimationEvent event = new AnimationEvent(row, type);
3080 event.headsUpFromBottom = onBottom;
3081 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003082 }
3083 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003084 mAddedHeadsUpChildren.clear();
3085 }
3086
Selim Cinekbbcebde2016-11-09 18:28:20 -08003087 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003088 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3089 return false;
3090 }
3091 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003092 }
3093
Selim Cinekb5605e52015-02-20 18:21:41 +01003094 private void generateGroupExpansionEvent() {
3095 // Generate a group expansion/collapsing event if there is such a group at all
3096 if (mExpandedGroupView != null) {
3097 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3098 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3099 mExpandedGroupView = null;
3100 }
3101 }
3102
Selim Cineka5e211b2014-08-11 17:35:48 +02003103 private void generateViewResizeEvent() {
3104 if (mNeedViewResizeAnimation) {
3105 mAnimationEvents.add(
3106 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3107 }
3108 mNeedViewResizeAnimation = false;
3109 }
3110
Selim Cinekeb973562014-05-02 17:07:49 +02003111 private void generateSnapBackEvents() {
3112 for (View child : mSnappedBackChildren) {
3113 mAnimationEvents.add(new AnimationEvent(child,
3114 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3115 }
3116 mSnappedBackChildren.clear();
3117 }
3118
3119 private void generateDragEvents() {
3120 for (View child : mDragAnimPendingChildren) {
3121 mAnimationEvents.add(new AnimationEvent(child,
3122 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3123 }
3124 mDragAnimPendingChildren.clear();
3125 }
3126
Selim Cinek572bbd42014-04-25 16:43:27 +02003127 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003128 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003129 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003130
3131 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003132 float removedTranslation = child.getTranslationY();
3133 boolean ignoreChildren = true;
3134 if (child instanceof ExpandableNotificationRow) {
3135 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3136 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3137 removedTranslation = row.getTranslationWhenRemoved();
3138 ignoreChildren = false;
3139 }
Selim Cinek51052042017-07-04 12:07:55 +02003140 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003141 }
Selim Cinek51052042017-07-04 12:07:55 +02003142 if (!childWasSwipedOut) {
3143 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003144 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Selim Cinek51052042017-07-04 12:07:55 +02003145 }
3146 int animationType = childWasSwipedOut
3147 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3148 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3149 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003150 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3151 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003152 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003153 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003154 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003155 mChildrenToRemoveAnimated.clear();
3156 }
3157
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003158 private void generatePositionChangeEvents() {
3159 for (View child : mChildrenChangingPositions) {
3160 mAnimationEvents.add(new AnimationEvent(child,
3161 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3162 }
3163 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003164 if (mGenerateChildOrderChangedEvent) {
3165 mAnimationEvents.add(new AnimationEvent(null,
3166 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3167 mGenerateChildOrderChangedEvent = false;
3168 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003169 }
3170
Selim Cinek572bbd42014-04-25 16:43:27 +02003171 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003172 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003173 if (mFromMoreCardAdditions.contains(child)) {
3174 mAnimationEvents.add(new AnimationEvent(child,
3175 AnimationEvent.ANIMATION_TYPE_ADD,
3176 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3177 } else {
3178 mAnimationEvents.add(new AnimationEvent(child,
3179 AnimationEvent.ANIMATION_TYPE_ADD));
3180 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003181 }
3182 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003183 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003184 }
3185
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003186 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003187 if (mTopPaddingNeedsAnimation) {
3188 mAnimationEvents.add(
3189 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
3190 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003191 mTopPaddingNeedsAnimation = false;
3192 }
3193
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003194 private void generateActivateEvent() {
3195 if (mActivateNeedsAnimation) {
3196 mAnimationEvents.add(
3197 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3198 }
3199 mActivateNeedsAnimation = false;
3200 }
3201
Selim Cinekd9acca52014-09-01 22:33:25 +02003202 private void generateAnimateEverythingEvent() {
3203 if (mEverythingNeedsAnimation) {
3204 mAnimationEvents.add(
3205 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3206 }
3207 mEverythingNeedsAnimation = false;
3208 }
3209
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003210 private void generateDimmedEvent() {
3211 if (mDimmedNeedsAnimation) {
3212 mAnimationEvents.add(
3213 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3214 }
3215 mDimmedNeedsAnimation = false;
3216 }
3217
Jorim Jaggiae441282014-08-01 02:45:18 +02003218 private void generateHideSensitiveEvent() {
3219 if (mHideSensitiveNeedsAnimation) {
3220 mAnimationEvents.add(
3221 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3222 }
3223 mHideSensitiveNeedsAnimation = false;
3224 }
3225
John Spurlockbf370992014-06-17 13:58:31 -04003226 private void generateDarkEvent() {
3227 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003228 AnimationEvent ev = new AnimationEvent(null,
3229 AnimationEvent.ANIMATION_TYPE_DARK,
3230 new AnimationFilter()
3231 .animateDark()
3232 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003233 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3234 mAnimationEvents.add(ev);
Selim Cinek972123d2016-05-03 14:25:58 -07003235 startBackgroundFadeIn();
John Spurlockbf370992014-06-17 13:58:31 -04003236 }
3237 mDarkNeedsAnimation = false;
3238 }
3239
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003240 private void generateGoToFullShadeEvent() {
3241 if (mGoToFullShadeNeedsAnimation) {
3242 mAnimationEvents.add(
3243 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3244 }
3245 mGoToFullShadeNeedsAnimation = false;
3246 }
3247
Selim Cinek67b22602014-03-10 15:40:16 +01003248 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003249 if (!isScrollingEnabled()) {
3250 return false;
3251 }
Selim Cinek67b22602014-03-10 15:40:16 +01003252 /*
3253 * This method JUST determines whether we want to intercept the motion.
3254 * If we return true, onMotionEvent will be called and we do the actual
3255 * scrolling there.
3256 */
3257
3258 /*
3259 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003260 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003261 * motion.
3262 */
3263 final int action = ev.getAction();
3264 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3265 return true;
3266 }
3267
Selim Cinek67b22602014-03-10 15:40:16 +01003268 switch (action & MotionEvent.ACTION_MASK) {
3269 case MotionEvent.ACTION_MOVE: {
3270 /*
3271 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003272 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003273 */
3274
3275 /*
3276 * Locally do absolute value. mLastMotionY is set to the y value
3277 * of the down event.
3278 */
3279 final int activePointerId = mActivePointerId;
3280 if (activePointerId == INVALID_POINTER) {
3281 // If we don't have a valid id, the touch down wasn't on content.
3282 break;
3283 }
3284
3285 final int pointerIndex = ev.findPointerIndex(activePointerId);
3286 if (pointerIndex == -1) {
3287 Log.e(TAG, "Invalid pointerId=" + activePointerId
3288 + " in onInterceptTouchEvent");
3289 break;
3290 }
3291
3292 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003293 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003294 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003295 final int xDiff = Math.abs(x - mDownX);
3296 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003297 setIsBeingDragged(true);
3298 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003299 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003300 initVelocityTrackerIfNotExists();
3301 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003302 }
3303 break;
3304 }
3305
3306 case MotionEvent.ACTION_DOWN: {
3307 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303308 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07003309 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01003310 setIsBeingDragged(false);
3311 recycleVelocityTracker();
3312 break;
3313 }
3314
3315 /*
3316 * Remember location of down touch.
3317 * ACTION_DOWN always refers to pointer index 0.
3318 */
3319 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003320 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003321 mActivePointerId = ev.getPointerId(0);
3322
3323 initOrResetVelocityTracker();
3324 mVelocityTracker.addMovement(ev);
3325 /*
3326 * If being flinged and user touches the screen, initiate drag;
3327 * otherwise don't. mScroller.isFinished should be false when
3328 * being flinged.
3329 */
3330 boolean isBeingDragged = !mScroller.isFinished();
3331 setIsBeingDragged(isBeingDragged);
3332 break;
3333 }
3334
3335 case MotionEvent.ACTION_CANCEL:
3336 case MotionEvent.ACTION_UP:
3337 /* Release the drag */
3338 setIsBeingDragged(false);
3339 mActivePointerId = INVALID_POINTER;
3340 recycleVelocityTracker();
3341 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08003342 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01003343 }
3344 break;
3345 case MotionEvent.ACTION_POINTER_UP:
3346 onSecondaryPointerUp(ev);
3347 break;
3348 }
3349
3350 /*
3351 * The only time we want to intercept motion events is if we are in the
3352 * drag mode.
3353 */
3354 return mIsBeingDragged;
3355 }
3356
Muyuan Li333a4fc2016-04-16 17:13:46 -07003357 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3358 return new StackScrollAlgorithm(context);
3359 }
3360
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003361 /**
3362 * @return Whether the specified motion event is actually happening over the content.
3363 */
3364 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003365 return isInContentBounds(event.getY());
3366 }
3367
3368 /**
3369 * @return Whether a y coordinate is inside the content.
3370 */
3371 public boolean isInContentBounds(float y) {
3372 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003373 }
3374
Selim Cinek67b22602014-03-10 15:40:16 +01003375 private void setIsBeingDragged(boolean isDragged) {
3376 mIsBeingDragged = isDragged;
3377 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003378 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003379 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003380 }
3381 }
3382
3383 @Override
3384 public void onWindowFocusChanged(boolean hasWindowFocus) {
3385 super.onWindowFocusChanged(hasWindowFocus);
3386 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003387 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003388 }
3389 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003390
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003391 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003392 public void clearChildFocus(View child) {
3393 super.clearChildFocus(child);
3394 if (mForcedScroll == child) {
3395 mForcedScroll = null;
3396 }
3397 }
3398
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003399 public void requestDisallowLongPress() {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003400 cancelLongPress();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003401 }
3402
Adrian Roosfa139752016-04-27 09:59:08 -07003403 public void requestDisallowDismiss() {
3404 mDisallowDismissInThisMotion = true;
3405 }
3406
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003407 public void cancelLongPress() {
3408 mSwipeHelper.cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02003409 }
3410
Selim Cinekfab078b2014-03-27 22:45:58 +01003411 @Override
3412 public boolean isScrolledToTop() {
3413 return mOwnScrollY == 0;
3414 }
3415
3416 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003417 public boolean isScrolledToBottom() {
3418 return mOwnScrollY >= getScrollRange();
3419 }
3420
3421 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003422 public View getHostView() {
3423 return this;
3424 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003425
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003426 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003427 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003428 }
3429
Mady Mellorc2dbe492017-03-30 13:22:03 -07003430 public void checkSnoozeLeavebehind() {
3431 if (mCheckForLeavebehind) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09003432 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
3433 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3434 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003435 mCheckForLeavebehind = false;
3436 }
3437 }
3438
3439 public void resetCheckSnoozeLeavebehind() {
3440 mCheckForLeavebehind = true;
3441 }
3442
Selim Cinek1685e632014-04-08 02:27:49 +02003443 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003444 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003445 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003446 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02003447 }
3448
3449 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003450 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07003451 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08003452 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003453 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003454 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003455 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003456 clearTemporaryViews();
3457 clearUserLockedViews();
3458 }
3459 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01003460
Selim Cinek5b1591a2017-07-03 17:05:01 +02003461 private void clearUserLockedViews() {
3462 for (int i = 0; i < getChildCount(); i++) {
3463 ExpandableView child = (ExpandableView) getChildAt(i);
3464 if (child instanceof ExpandableNotificationRow) {
3465 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3466 row.setUserLocked(false);
3467 }
3468 }
3469 }
3470
3471 private void clearTemporaryViews() {
3472 // lets make sure nothing is in the overlay / transient anymore
3473 clearTemporaryViews(this);
3474 for (int i = 0; i < getChildCount(); i++) {
3475 ExpandableView child = (ExpandableView) getChildAt(i);
3476 if (child instanceof ExpandableNotificationRow) {
3477 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3478 clearTemporaryViews(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003479 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003480 }
Selim Cinek1685e632014-04-08 02:27:49 +02003481 }
3482
Selim Cineka5703182016-05-11 21:23:16 -04003483 private void clearTemporaryViews(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003484 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003485 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003486 }
Selim Cineka5703182016-05-11 21:23:16 -04003487 if (viewGroup != null) {
3488 viewGroup.getOverlay().clear();
3489 }
Selim Cinekd1395642016-04-28 12:22:42 -07003490 }
3491
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003492 public void onPanelTrackingStarted() {
3493 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003494 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003495 }
3496 public void onPanelTrackingStopped() {
3497 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003498 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003499 }
3500
Selim Cinekb24e0a92015-06-09 20:17:30 -07003501 public void resetScrollPosition() {
3502 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003503 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003504 }
3505
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003506 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003507 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003508 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003509 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003510 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003511 if (!mIsExpanded) {
3512 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003513 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08003514 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003515 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003516 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003517 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003518 }
3519 }
3520
3521 private void updateChronometers() {
3522 int childCount = getChildCount();
3523 for (int i = 0; i < childCount; i++) {
3524 updateChronometerForChild(getChildAt(i));
3525 }
3526 }
3527
3528 private void updateChronometerForChild(View child) {
3529 if (child instanceof ExpandableNotificationRow) {
3530 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3531 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003532 }
Selim Cinek1685e632014-04-08 02:27:49 +02003533 }
3534
Jorim Jaggibe565df2014-04-28 17:51:23 +02003535 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003536 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003537 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003538 updateScrollPositionOnExpandInBottom(view);
3539 clampScrollPosition();
Selim Cinekaef92ef2014-06-06 18:06:04 +02003540 notifyHeightChangeListener(view);
Selim Cinekbc243a92016-09-27 16:35:13 -07003541 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3542 ? (ExpandableNotificationRow) view
3543 : null;
3544 if (row != null && (row == mFirstVisibleBackgroundChild
3545 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3546 updateAlgorithmLayoutMinHeight();
3547 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003548 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003549 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003550 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003551 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003552 }
3553
Selim Cineka5e211b2014-08-11 17:35:48 +02003554 @Override
3555 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003556 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003557 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003558 }
3559
Selim Cinekf7a14c02014-07-07 14:01:46 +02003560 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07003561 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02003562 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003563 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003564 if (row.isSummaryWithChildren()) {
3565 return;
3566 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003567 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003568 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003569 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003570 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003571 }
Selim Cinekdb167372016-11-17 15:41:17 -08003572 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Anthony Chen9e05d462017-04-07 10:10:21 -07003573 if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08003574 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3575 }
3576 if (endPosition > layoutEnd) {
3577 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003578 mDisallowScrollingInThisMotion = true;
3579 }
3580 }
3581 }
3582 }
3583
Jorim Jaggibe565df2014-04-28 17:51:23 +02003584 public void setOnHeightChangedListener(
3585 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3586 this.mOnHeightChangedListener = mOnHeightChangedListener;
3587 }
3588
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003589 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3590 mOnEmptySpaceClickListener = listener;
3591 }
3592
Selim Cinek572bbd42014-04-25 16:43:27 +02003593 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003594 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003595 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003596 runAnimationFinishedRunnables();
Selim Cinek0fccc722015-07-29 17:04:36 -07003597 clearViewOverlays();
Selim Cinek8fc78752016-07-13 14:34:56 -07003598 clearHeadsUpDisappearRunning();
3599 }
3600
3601 private void clearHeadsUpDisappearRunning() {
3602 for (int i = 0; i < getChildCount(); i++) {
3603 View view = getChildAt(i);
3604 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003605 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003606 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003607 if (row.isSummaryWithChildren()) {
3608 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003609 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003610 }
3611 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003612 }
3613 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003614 }
3615
3616 private void clearViewOverlays() {
3617 for (View view : mClearOverlayViewsWhenFinished) {
Selim Cineka5703182016-05-11 21:23:16 -04003618 StackStateAnimator.removeFromOverlay(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003619 }
Selim Cinekc8df0642017-02-23 17:01:04 -08003620 mClearOverlayViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003621 }
3622
3623 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003624 for (Runnable runnable : mAnimationFinishedRunnables) {
3625 runnable.run();
3626 }
3627 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003628 }
3629
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003630 /**
3631 * See {@link AmbientState#setDimmed}.
3632 */
3633 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07003634 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003635 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003636 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003637 mDimmedNeedsAnimation = true;
3638 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003639 animateDimmed(dimmed);
3640 } else {
3641 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003642 }
3643 requestChildrenUpdate();
3644 }
3645
Selim Cinek8a9308d2017-08-24 09:31:08 -07003646 @VisibleForTesting
3647 boolean isDimmed() {
3648 return mAmbientState.isDimmed();
3649 }
3650
Selim Cinekd35c2792016-01-21 13:20:57 -08003651 private void setDimAmount(float dimAmount) {
3652 mDimAmount = dimAmount;
3653 updateBackgroundDimming();
3654 }
3655
3656 private void animateDimmed(boolean dimmed) {
3657 if (mDimAnimator != null) {
3658 mDimAnimator.cancel();
3659 }
3660 float target = dimmed ? 1.0f : 0.0f;
3661 if (target == mDimAmount) {
3662 return;
3663 }
3664 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3665 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3666 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3667 mDimAnimator.addListener(mDimEndListener);
3668 mDimAnimator.addUpdateListener(mDimUpdateListener);
3669 mDimAnimator.start();
3670 }
3671
Jorim Jaggiae441282014-08-01 02:45:18 +02003672 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3673 if (hideSensitive != mAmbientState.isHideSensitive()) {
3674 int childCount = getChildCount();
3675 for (int i = 0; i < childCount; i++) {
3676 ExpandableView v = (ExpandableView) getChildAt(i);
3677 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3678 }
3679 mAmbientState.setHideSensitive(hideSensitive);
3680 if (animate && mAnimationsEnabled) {
3681 mHideSensitiveNeedsAnimation = true;
3682 mNeedsAnimation = true;
3683 }
3684 requestChildrenUpdate();
3685 }
3686 }
3687
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003688 /**
3689 * See {@link AmbientState#setActivatedChild}.
3690 */
Selim Cineka32ab602014-06-11 15:06:01 +02003691 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003692 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003693 if (mAnimationsEnabled) {
3694 mActivateNeedsAnimation = true;
3695 mNeedsAnimation = true;
3696 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003697 requestChildrenUpdate();
3698 }
3699
Selim Cineka32ab602014-06-11 15:06:01 +02003700 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003701 return mAmbientState.getActivatedChild();
3702 }
3703
Selim Cinek572bbd42014-04-25 16:43:27 +02003704 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003705 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003706 if (mListener != null) {
3707 mListener.onChildLocationsChanged(this);
3708 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003709 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003710 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003711 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003712 updateViewShadows();
3713 }
3714
3715 private void updateViewShadows() {
3716 // we need to work around an issue where the shadow would not cast between siblings when
3717 // their z difference is between 0 and 0.1
3718
3719 // Lefts first sort by Z difference
3720 for (int i = 0; i < getChildCount(); i++) {
3721 ExpandableView child = (ExpandableView) getChildAt(i);
3722 if (child.getVisibility() != GONE) {
3723 mTmpSortedChildren.add(child);
3724 }
3725 }
3726 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3727
3728 // Now lets update the shadow for the views
3729 ExpandableView previous = null;
3730 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3731 ExpandableView expandableView = mTmpSortedChildren.get(i);
3732 float translationZ = expandableView.getTranslationZ();
3733 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3734 float diff = otherZ - translationZ;
3735 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3736 // There is no fake shadow to be drawn
3737 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3738 } else {
3739 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003740 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3741 expandableView.setFakeShadowIntensity(
3742 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003743 previous.getOutlineAlpha(), (int) yLocation,
3744 previous.getOutlineTranslation());
3745 }
3746 previous = expandableView;
3747 }
3748
3749 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003750 }
3751
Lucas Dupine17ce522017-07-17 15:45:06 -07003752 /**
3753 * Update colors of "dismiss" and "empty shade" views.
3754 *
3755 * @param lightTheme True if light theme should be used.
3756 */
3757 public void updateDecorViews(boolean lightTheme) {
3758 if (lightTheme == mUsingLightTheme) {
3759 return;
3760 }
3761 mUsingLightTheme = lightTheme;
3762 Context context = new ContextThemeWrapper(mContext,
3763 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
3764 final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
3765 mDismissView.setTextColor(textColor);
3766 mEmptyShadeView.setTextColor(textColor);
3767 }
3768
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003769 public void goToFullShade(long delay) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003770 if (mDismissView != null) {
3771 mDismissView.setInvisible();
3772 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003773 mEmptyShadeView.setInvisible();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003774 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003775 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003776 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003777 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003778 }
3779
Selim Cinek1408eb52014-06-02 14:45:38 +02003780 public void cancelExpandHelper() {
3781 mExpandHelper.cancel();
3782 }
3783
3784 public void setIntrinsicPadding(int intrinsicPadding) {
3785 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07003786 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02003787 }
3788
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003789 public int getIntrinsicPadding() {
3790 return mIntrinsicPadding;
3791 }
3792
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003793 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003794 * @return the y position of the first notification
3795 */
3796 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003797 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003798 }
3799
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003800 @Override
3801 public boolean shouldDelayChildPressedState() {
3802 return true;
3803 }
3804
Jorim Jaggi457cc352014-06-02 22:47:42 +02003805 /**
John Spurlockbf370992014-06-17 13:58:31 -04003806 * See {@link AmbientState#setDark}.
3807 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003808 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02003809 if (mAmbientState.isDark() == dark) {
3810 return;
3811 }
John Spurlockbf370992014-06-17 13:58:31 -04003812 mAmbientState.setDark(dark);
3813 if (animate && mAnimationsEnabled) {
3814 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003815 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
John Spurlockbf370992014-06-17 13:58:31 -04003816 mNeedsAnimation = true;
Selim Cinek972123d2016-05-03 14:25:58 -07003817 setBackgroundFadeAmount(0.0f);
3818 } else if (!dark) {
3819 setBackgroundFadeAmount(1.0f);
John Spurlockbf370992014-06-17 13:58:31 -04003820 }
3821 requestChildrenUpdate();
Selim Cinek6811d722016-01-19 17:53:12 -08003822 if (dark) {
Selim Cinek6811d722016-01-19 17:53:12 -08003823 mScrimController.setExcludedBackgroundArea(null);
3824 } else {
3825 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08003826 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003827
3828 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003829 updateContentHeight();
3830 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04003831 }
3832
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003833 /**
3834 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
3835 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
3836 * {@link #mAmbientState}'s dark mode is toggled.
3837 */
3838 private void updateWillNotDraw() {
Adrian Roosf0b4f962017-05-25 11:53:11 -07003839 boolean willDraw = !mAmbientState.isDark() && mShouldDrawNotificationBackground || DEBUG;
3840 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003841 }
3842
Selim Cinek972123d2016-05-03 14:25:58 -07003843 private void setBackgroundFadeAmount(float fadeAmount) {
3844 mBackgroundFadeAmount = fadeAmount;
3845 updateBackgroundDimming();
3846 }
3847
3848 public float getBackgroundFadeAmount() {
3849 return mBackgroundFadeAmount;
3850 }
3851
3852 private void startBackgroundFadeIn() {
3853 ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, BACKGROUND_FADE, 0f, 1f);
Adrian Roos28f90c72017-05-08 17:24:26 -07003854 fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
Selim Cinek972123d2016-05-03 14:25:58 -07003855 fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
3856 fadeAnimator.start();
3857 }
3858
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003859 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07003860 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003861 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3862 }
3863 if (screenLocation.y > getBottomMostNotificationBottom()) {
3864 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
3865 }
3866 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
3867 if (child != null) {
3868 return getNotGoneIndex(child);
3869 } else {
3870 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3871 }
3872 }
3873
3874 private int getNotGoneIndex(View child) {
3875 int count = getChildCount();
3876 int notGoneIndex = 0;
3877 for (int i = 0; i < count; i++) {
3878 View v = getChildAt(i);
3879 if (child == v) {
3880 return notGoneIndex;
3881 }
3882 if (v.getVisibility() != View.GONE) {
3883 notGoneIndex++;
3884 }
3885 }
3886 return -1;
3887 }
3888
Anthony Chen5e3742e2017-04-07 14:28:44 -07003889 public void setDismissView(@NonNull DismissView dismissView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003890 int index = -1;
3891 if (mDismissView != null) {
3892 index = indexOfChild(mDismissView);
3893 removeView(mDismissView);
3894 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003895 mDismissView = dismissView;
Selim Cinek01af3342016-02-09 19:25:31 -08003896 addView(mDismissView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003897 }
3898
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003899 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003900 int index = -1;
3901 if (mEmptyShadeView != null) {
3902 index = indexOfChild(mEmptyShadeView);
3903 removeView(mEmptyShadeView);
3904 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003905 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08003906 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003907 }
3908
3909 public void updateEmptyShadeView(boolean visible) {
3910 int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
3911 int newVisibility = visible ? VISIBLE : GONE;
3912 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003913 if (newVisibility != GONE) {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003914 if (mEmptyShadeView.willBeGone()) {
3915 mEmptyShadeView.cancelAnimation();
3916 } else {
3917 mEmptyShadeView.setInvisible();
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003918 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003919 mEmptyShadeView.setVisibility(newVisibility);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003920 mEmptyShadeView.setWillBeGone(false);
3921 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003922 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003923 } else {
Selim Cinek20867102014-12-10 17:09:17 +01003924 Runnable onFinishedRunnable = new Runnable() {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003925 @Override
3926 public void run() {
3927 mEmptyShadeView.setVisibility(GONE);
3928 mEmptyShadeView.setWillBeGone(false);
3929 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003930 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003931 }
Selim Cinek20867102014-12-10 17:09:17 +01003932 };
Selim Cineka9d3efd2016-08-16 14:16:59 -07003933 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek20867102014-12-10 17:09:17 +01003934 mEmptyShadeView.setWillBeGone(true);
3935 mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
3936 } else {
3937 mEmptyShadeView.setInvisible();
3938 onFinishedRunnable.run();
3939 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003940 }
3941 }
3942 }
3943
Dan Sandlereceda3d2014-07-21 15:35:01 -04003944 public void updateDismissView(boolean visible) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003945 if (mDismissView == null) {
3946 return;
3947 }
3948
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003949 int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003950 int newVisibility = visible ? VISIBLE : GONE;
3951 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003952 if (newVisibility != GONE) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003953 if (mDismissView.willBeGone()) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003954 mDismissView.cancelAnimation();
3955 } else {
3956 mDismissView.setInvisible();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003957 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003958 mDismissView.setVisibility(newVisibility);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003959 mDismissView.setWillBeGone(false);
3960 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02003961 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003962 } else {
Selim Cinek7d5f3742014-11-07 18:07:49 +01003963 Runnable dimissHideFinishRunnable = new Runnable() {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003964 @Override
3965 public void run() {
3966 mDismissView.setVisibility(GONE);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003967 mDismissView.setWillBeGone(false);
3968 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02003969 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003970 }
Selim Cinek7d5f3742014-11-07 18:07:49 +01003971 };
Selim Cinek20867102014-12-10 17:09:17 +01003972 if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
Selim Cinek7d5f3742014-11-07 18:07:49 +01003973 mDismissView.setWillBeGone(true);
3974 mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
3975 } else {
3976 dimissHideFinishRunnable.run();
Selim Cinek7d5f3742014-11-07 18:07:49 +01003977 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003978 }
3979 }
3980 }
3981
3982 public void setDismissAllInProgress(boolean dismissAllInProgress) {
3983 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07003984 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07003985 handleDismissAllClipping();
3986 }
3987
3988 private void handleDismissAllClipping() {
3989 final int count = getChildCount();
3990 boolean previousChildWillBeDismissed = false;
3991 for (int i = 0; i < count; i++) {
3992 ExpandableView child = (ExpandableView) getChildAt(i);
3993 if (child.getVisibility() == GONE) {
3994 continue;
3995 }
3996 if (mDismissAllInProgress && previousChildWillBeDismissed) {
3997 child.setMinClipTopAmount(child.getClipTopAmount());
3998 } else {
3999 child.setMinClipTopAmount(0);
4000 }
4001 previousChildWillBeDismissed = canChildBeDismissed(child);
4002 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004003 }
4004
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004005 public boolean isDismissViewNotGone() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004006 return mDismissView != null
4007 && mDismissView.getVisibility() != View.GONE
4008 && !mDismissView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004009 }
4010
4011 public boolean isDismissViewVisible() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004012 return mDismissView != null && mDismissView.isVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004013 }
4014
4015 public int getDismissViewHeight() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004016 return mDismissView == null ? 0 : mDismissView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004017 }
4018
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004019 public int getEmptyShadeViewHeight() {
4020 return mEmptyShadeView.getHeight();
4021 }
4022
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004023 public float getBottomMostNotificationBottom() {
4024 final int count = getChildCount();
4025 float max = 0;
4026 for (int childIdx = 0; childIdx < count; childIdx++) {
4027 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4028 if (child.getVisibility() == GONE) {
4029 continue;
4030 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004031 float bottom = child.getTranslationY() + child.getActualHeight()
4032 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004033 if (bottom > max) {
4034 max = bottom;
4035 }
4036 }
Selim Cinekd2281152015-04-10 14:37:46 -07004037 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004038 }
4039
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004040 public void setStatusBar(StatusBar statusBar) {
4041 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004042 }
4043
Selim Cinekb5605e52015-02-20 18:21:41 +01004044 public void setGroupManager(NotificationGroupManager groupManager) {
4045 this.mGroupManager = groupManager;
4046 }
4047
Selim Cinekd9acca52014-09-01 22:33:25 +02004048 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01004049 requestAnimateEverything();
4050 }
4051
4052 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004053 if (mIsExpanded && mAnimationsEnabled) {
4054 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004055 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004056 requestChildrenUpdate();
4057 }
4058 }
4059
Selim Cinek04fb2582015-06-02 19:58:09 +02004060 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004061 int childCount = getChildCount();
4062 for (int i = childCount - 1; i >= 0; i--) {
4063 ExpandableView child = (ExpandableView) getChildAt(i);
4064 if (child.getVisibility() != View.GONE) {
4065 float childTop = child.getY();
4066 if (childTop > touchY) {
4067 // we are above a notification entirely let's abort
4068 return false;
4069 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004070 boolean belowChild = touchY > childTop + child.getActualHeight()
4071 - child.getClipBottomAmount();
Selim Cinekabf60bb2015-02-20 17:36:10 +01004072 if (child == mDismissView) {
4073 if(!belowChild && !mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
4074 touchY - childTop)) {
4075 // We clicked on the dismiss button
4076 return false;
4077 }
4078 } else if (child == mEmptyShadeView) {
4079 // We arrived at the empty shade view, for which we accept all clicks
4080 return true;
4081 } else if (!belowChild){
4082 // We are on a child
4083 return false;
4084 }
4085 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004086 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004087 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004088 }
4089
Selim Cinekb5605e52015-02-20 18:21:41 +01004090 @Override
4091 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004092 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4093 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004094 if (animated) {
4095 mExpandedGroupView = changedRow;
4096 mNeedsAnimation = true;
4097 }
4098 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004099 if (!mGroupExpandedForMeasure) {
4100 onHeightChanged(changedRow, false /* needsAnimation */);
4101 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004102 runAfterAnimationFinished(new Runnable() {
4103 @Override
4104 public void run() {
4105 changedRow.onFinishedExpansionChange();
4106 }
4107 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004108 }
4109
4110 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004111 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004112 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004113 }
4114
Selim Cinekc22fff62016-05-20 12:44:30 -07004115 /** @hide */
4116 @Override
4117 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4118 super.onInitializeAccessibilityEventInternal(event);
4119 event.setScrollable(mScrollable);
4120 event.setScrollX(mScrollX);
4121 event.setScrollY(mOwnScrollY);
4122 event.setMaxScrollX(mScrollX);
4123 event.setMaxScrollY(getScrollRange());
4124 }
4125
4126 @Override
4127 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4128 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004129 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004130 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004131 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004132 info.addAction(
4133 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4134 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4135 }
Selim Cinekef406062016-09-29 17:33:13 -07004136 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004137 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4138 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4139 }
4140 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004141 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4142 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004143 }
4144
4145 /** @hide */
4146 @Override
4147 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4148 if (super.performAccessibilityActionInternal(action, arguments)) {
4149 return true;
4150 }
4151 if (!isEnabled()) {
4152 return false;
4153 }
4154 int direction = -1;
4155 switch (action) {
4156 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4157 // fall through
4158 case android.R.id.accessibilityActionScrollDown:
4159 direction = 1;
4160 // fall through
4161 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4162 // fall through
4163 case android.R.id.accessibilityActionScrollUp:
4164 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004165 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004166 final int targetScrollY = Math.max(0,
4167 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4168 if (targetScrollY != mOwnScrollY) {
4169 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004170 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004171 return true;
4172 }
4173 break;
4174 }
4175 return false;
4176 }
4177
Selim Cinekef5127e2015-12-21 16:55:58 -08004178 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004179 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004180 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004181 }
4182
4183 public void generateChildOrderChangedEvent() {
4184 if (mIsExpanded && mAnimationsEnabled) {
4185 mGenerateChildOrderChangedEvent = true;
4186 mNeedsAnimation = true;
4187 requestChildrenUpdate();
4188 }
4189 }
4190
Selim Cinek684a4422015-04-15 16:18:39 -07004191 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004192 mAnimationFinishedRunnables.add(runnable);
4193 }
4194
4195 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
4196 mHeadsUpManager = headsUpManager;
4197 mAmbientState.setHeadsUpManager(headsUpManager);
4198 }
4199
4200 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004201 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004202 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4203 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004204 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004205 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004206 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004207 requestChildrenUpdate();
4208 }
4209 }
4210
4211 public void setShadeExpanded(boolean shadeExpanded) {
4212 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004213 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004214 }
4215
Selim Cineka59ecc32015-04-07 10:51:49 -07004216 /**
4217 * Set the boundary for the bottom heads up position. The heads up will always be above this
4218 * position.
4219 *
4220 * @param height the height of the screen
4221 * @param bottomBarHeight the height of the bar on the bottom
4222 */
4223 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4224 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4225 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004226 requestChildrenUpdate();
4227 }
4228
4229 public void setTrackingHeadsUp(boolean trackingHeadsUp) {
4230 mTrackingHeadsUp = trackingHeadsUp;
4231 }
4232
Selim Cinekaac93252015-04-14 20:04:12 -07004233 public void setScrimController(ScrimController scrimController) {
4234 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004235 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004236 }
4237
Selim Cinekbbc580b2015-06-03 14:11:03 +02004238 public void forceNoOverlappingRendering(boolean force) {
4239 mForceNoOverlappingRendering = force;
4240 }
4241
4242 @Override
4243 public boolean hasOverlappingRendering() {
4244 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4245 }
4246
Selim Cinek6811d722016-01-19 17:53:12 -08004247 public void setAnimationRunning(boolean animationRunning) {
4248 if (animationRunning != mAnimationRunning) {
4249 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004250 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004251 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004252 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004253 }
4254 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004255 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004256 }
4257 }
4258
Selim Cinek3776fe02016-02-04 13:32:43 -08004259 public boolean isExpanded() {
4260 return mIsExpanded;
4261 }
4262
Adrian Roos7d062c42017-03-30 15:11:43 -07004263 public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
4264 if (mPulsing == null && pulsing == null) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004265 return;
4266 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004267 mPulsing = pulsing;
Selim Cinekebf42342017-07-13 15:46:10 +02004268 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004269 updateNotificationAnimationStates();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004270 updateContentHeight();
4271 notifyHeightChangeListener(mShelf);
Adrian Roosd83e9992017-03-16 15:17:57 -07004272 requestChildrenUpdate();
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004273 }
4274
Selim Cinek07304f5222016-05-19 18:31:36 -07004275 public void setFadingOut(boolean fadingOut) {
4276 if (fadingOut != mFadingOut) {
4277 mFadingOut = fadingOut;
4278 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004279 }
4280 }
4281
Selim Cinek48ff9b42016-11-09 19:31:51 -08004282 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4283 if (mScrimController == null) {
4284 // we're not set up yet.
4285 return;
4286 }
4287 if (parentNotFullyVisible != mParentNotFullyVisible) {
4288 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004289 updateFadingState();
4290 }
4291 }
4292
4293 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004294 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004295 updateSrcDrawing();
4296 }
4297
Selim Cinek31d37b92016-04-26 09:56:42 -07004298 @Override
4299 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4300 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004301 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004302 }
4303
Selim Cinekbc243a92016-09-27 16:35:13 -07004304 public void setQsExpanded(boolean qsExpanded) {
4305 mQsExpanded = qsExpanded;
4306 updateAlgorithmLayoutMinHeight();
4307 }
4308
Selim Cinekef406062016-09-29 17:33:13 -07004309 public void setOwnScrollY(int ownScrollY) {
4310 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004311 // We still want to call the normal scrolled changed for accessibility reasons
4312 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004313 mOwnScrollY = ownScrollY;
4314 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004315 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004316 }
4317 }
4318
Selim Cinek281c2022016-10-13 19:14:43 -07004319 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004320 int index = -1;
4321 if (mShelf != null) {
4322 index = indexOfChild(mShelf);
4323 removeView(mShelf);
4324 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004325 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004326 addView(mShelf, index);
4327 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004328 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004329 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004330 }
4331
4332 public NotificationShelf getNotificationShelf() {
4333 return mShelf;
4334 }
4335
Selim Cinekad7fac02016-10-18 17:09:15 -07004336 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4337 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4338 mMaxDisplayedNotifications = maxDisplayedNotifications;
4339 updateContentHeight();
4340 notifyHeightChangeListener(mShelf);
4341 }
4342 }
4343
Selim Cinek48ff9b42016-11-09 19:31:51 -08004344 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004345 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004346 }
4347
Selim Cinekcafa87f2016-10-26 17:00:17 -07004348 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4349 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4350 updateClipping();
4351 }
4352
4353 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4354 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4355 updateClipping();
4356 }
4357
Selim Cinek355652a2016-12-07 13:32:12 -08004358 public void setStatusBarState(int statusBarState) {
4359 mStatusBarState = statusBarState;
4360 mAmbientState.setStatusBarState(statusBarState);
Selim Cinekd9b7dd42017-11-10 17:53:47 -08004361 applyRoundedNess();
Selim Cinek515b2032017-11-15 10:20:19 -08004362 updateSidePaddings();
4363 }
4364
4365 private void updateSidePaddings() {
4366 int sidePaddings = mStatusBarState == StatusBarState.KEYGUARD ? mLockscreenSidePaddings : 0;
4367 if (sidePaddings != mSidePaddings) {
4368 boolean animate = isShown();
4369 mSidePaddings = sidePaddings;
4370 PropertyAnimator.setProperty(this, SIDE_PADDINGS, sidePaddings,
4371 SIDE_PADDING_PROPERTIES, animate);
4372 }
4373 }
4374
4375 protected void setCurrentSidePadding(float sidePadding) {
4376 mCurrentSidePadding = sidePadding;
4377 updateBackground();
4378 applySidePaddingsToChildren();
4379 }
4380
4381 private void applySidePaddingsToChildren() {
4382 for (int i = 0; i < getChildCount(); i++) {
4383 View view = getChildAt(i);
4384 updateCurrentSidePaddings(view);
4385 }
4386 }
4387
4388 private void updateCurrentSidePaddings(View view) {
4389 if (!(view instanceof ExpandableOutlineView)) {
4390 return;
4391 }
4392 ExpandableOutlineView outlineView = (ExpandableOutlineView) view;
4393 outlineView.setCurrentSidePaddings(mCurrentSidePadding);
4394 }
4395
4396 protected float getCurrentSidePadding() {
4397 return mCurrentSidePadding;
Selim Cinek355652a2016-12-07 13:32:12 -08004398 }
4399
Selim Cinekd5ab6452016-12-08 16:34:00 -08004400 public void setExpandingVelocity(float expandingVelocity) {
4401 mAmbientState.setExpandingVelocity(expandingVelocity);
4402 }
4403
Selim Cinekfcff4c62016-12-27 14:26:06 +01004404 public float getOpeningHeight() {
4405 if (mEmptyShadeView.getVisibility() == GONE) {
4406 return getMinExpansionHeight();
4407 } else {
4408 return getAppearEndPosition();
4409 }
4410 }
4411
4412 public void setIsFullWidth(boolean isFullWidth) {
4413 mAmbientState.setPanelFullWidth(isFullWidth);
4414 }
4415
Selim Cinekec29d342017-05-05 18:31:49 -07004416 public void setUnlockHintRunning(boolean running) {
4417 mAmbientState.setUnlockHintRunning(running);
4418 }
4419
Selim Cinek5cf1d052017-06-01 17:36:46 -07004420 public void setQsCustomizerShowing(boolean isShowing) {
4421 mAmbientState.setQsCustomizerShowing(isShowing);
4422 requestChildrenUpdate();
4423 }
4424
4425 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4426 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4427 }
4428
Adrian Roosdc747bd2017-06-01 16:09:15 -07004429 public void setDarkShelfOffsetX(int shelfOffsetX) {
4430 mShelf.setDarkOffsetX(shelfOffsetX);
4431 }
4432
Selim Cinek707e2072017-06-30 18:32:40 +02004433 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4434 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
4435 + " alpha:%f scrollY:%d]",
4436 this.getClass().getSimpleName(),
4437 mPulsing != null ?"T":"f",
4438 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4439 getVisibility() == View.VISIBLE ? "visible"
4440 : getVisibility() == View.GONE ? "gone"
4441 : "invisible",
4442 getAlpha(),
4443 mAmbientState.getScrollY()));
4444 }
4445
Selim Cinekd95ca7c2017-07-26 12:20:38 -07004446 public void setTouchActive(boolean touchActive) {
4447 mShelf.setTouchActive(touchActive);
4448 }
4449
Selim Cinek7103fd42016-05-09 22:22:33 -04004450 /**
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004451 * A listener that is notified when some child locations might have changed.
4452 */
4453 public interface OnChildLocationsChangedListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004454 void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout);
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004455 }
Selim Cinek572bbd42014-04-25 16:43:27 +02004456
Jorim Jaggi290600a2014-05-30 17:02:20 +02004457 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004458 * A listener that is notified when the empty space below the notifications is clicked on
4459 */
4460 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004461 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004462 }
4463
4464 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004465 * A listener that gets notified when the overscroll at the top has changed.
4466 */
4467 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004468
4469 /**
4470 * Notifies a listener that the overscroll has changed.
4471 *
4472 * @param amount the amount of overscroll, in pixels
4473 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4474 * unrubberbanded motion to directly expand overscroll view (e.g expand
4475 * QS)
4476 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004477 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004478
4479 /**
4480 * Notify a listener that the scroller wants to escape from the scrolling motion and
4481 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4482 *
4483 * @param velocity The velocity that the Scroller had when over flinging
4484 * @param open Should the fling open or close the overscroll view.
4485 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004486 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004487 }
4488
Mady Mellor95d743c2017-01-10 12:05:27 -08004489 private class NotificationSwipeHelper extends SwipeHelper
4490 implements NotificationSwipeActionHelper {
4491 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004492 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004493 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004494
4495 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4496 super(swipeDirection, callback, context);
4497 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004498 mFalsingCheck = new Runnable() {
4499 @Override
4500 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004501 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004502 }
4503 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004504 }
4505
4506 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004507 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004508 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004509 if (mCurrMenuRow != null) {
4510 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004511 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004512 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004513 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004514
Mady Mellor95d743c2017-01-10 12:05:27 -08004515 // Slide back any notifications that might be showing a menu
4516 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004517
4518 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004519 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
4520 mCurrMenuRow = row.createMenu();
4521 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4522 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor89e15ec2017-06-28 17:08:21 -07004523 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004524 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004525 }
4526
4527 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004528 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004529 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004530 if (mCurrMenuRow != null) {
4531 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004532 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004533 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004534
Mady Mellor95d743c2017-01-10 12:05:27 -08004535 @Override
4536 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4537 float translation) {
4538 if (mCurrMenuRow != null) {
4539 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004540 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004541 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004542 }
4543
4544 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004545 public void dismissChild(final View view, float velocity,
4546 boolean useAccelerateInterpolator) {
4547 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004548 if (mIsExpanded) {
4549 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4550 // of the panel early.
4551 handleChildDismissed(view);
4552 }
Eliot Courtney47098cb2017-10-18 17:30:30 +09004553 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
4554 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4555 false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004556 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004557 }
4558
4559 @Override
4560 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004561 super.snapChild(animView, targetLeft, velocity);
4562 onDragCancelled(animView);
4563 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004564 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004565 }
4566 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004567
4568 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004569 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4570 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004571 }
4572
Mady Mellorbd707492017-05-10 17:51:25 -07004573 public boolean isFalseGesture(MotionEvent ev) {
4574 return super.isFalseGesture(ev);
4575 }
4576
Mady Mellor95d743c2017-01-10 12:05:27 -08004577 private void handleMenuCoveredOrDismissed() {
4578 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4579 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004580 }
4581 }
4582
Mady Mellor4b80b102016-01-22 08:03:58 -08004583 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004584 public Animator getViewTranslationAnimator(View v, float target,
4585 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004586 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004587 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4588 } else {
4589 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004590 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004591 }
4592
4593 @Override
4594 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004595 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004596 }
4597
4598 @Override
4599 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004600 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004601 }
4602
Mady Mellor95d743c2017-01-10 12:05:27 -08004603 @Override
4604 public void dismiss(View animView, float velocity) {
4605 dismissChild(animView, velocity,
4606 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4607 }
4608
4609 @Override
4610 public void snap(View animView, float targetLeft, float velocity) {
4611 snapChild(animView, targetLeft, velocity);
4612 }
4613
4614 @Override
4615 public boolean swipedFarEnough(float translation, float viewSize) {
4616 return swipedFarEnough();
4617 }
4618
4619 @Override
4620 public boolean swipedFastEnough(float translation, float velocity) {
4621 return swipedFastEnough();
4622 }
4623
4624 @Override
4625 public float getMinDismissVelocity() {
4626 return getEscapeVelocity();
4627 }
4628
4629 public void onMenuShown(View animView) {
4630 onDragCancelled(animView);
4631
4632 // If we're on the lockscreen we want to false this.
4633 if (isAntiFalsingNeeded()) {
4634 mHandler.removeCallbacks(mFalsingCheck);
4635 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4636 }
4637 }
4638
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004639 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09004640 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004641 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004642 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4643 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004644 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004645 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004646 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004647 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004648 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004649 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004650 if (view != null && !isTouchInView(ev, view)) {
4651 // Touch was outside visible guts / menu notification, close what's visible
Eliot Courtney47098cb2017-10-18 17:30:30 +09004652 mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
4653 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4654 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004655 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004656 }
4657 }
4658
Mady Mellor95d743c2017-01-10 12:05:27 -08004659 public void resetExposedMenuView(boolean animate, boolean force) {
4660 if (mMenuExposedView == null
4661 || (!force && mMenuExposedView == mTranslatingParentView)) {
4662 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004663 return;
4664 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004665 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004666 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004667 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004668 0 /* leftTarget */, null /* updateListener */);
4669 if (anim != null) {
4670 anim.start();
4671 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004672 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
Selim Cinekb2e0f332017-08-18 12:24:38 -07004673 ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
4674 if (!row.isRemoved()) {
4675 row.resetTranslation();
4676 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004677 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004678 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004679 }
4680 }
4681
Mady Mellorc2dbe492017-03-30 13:22:03 -07004682 private boolean isTouchInView(MotionEvent ev, View view) {
4683 if (view == null) {
4684 return false;
4685 }
4686 final int height = (view instanceof ExpandableView)
4687 ? ((ExpandableView) view).getActualHeight()
4688 : view.getHeight();
4689 final int rx = (int) ev.getRawX();
4690 final int ry = (int) ev.getRawY();
4691 view.getLocationOnScreen(mTempInt2);
4692 final int x = mTempInt2[0];
4693 final int y = mTempInt2[1];
4694 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4695 boolean ret = rect.contains(rx, ry);
4696 return ret;
4697 }
4698
Selim Cinek33223572016-02-19 19:32:22 -08004699 private void updateContinuousShadowDrawing() {
4700 boolean continuousShadowUpdate = mAnimationRunning
4701 || !mAmbientState.getDraggedViews().isEmpty();
4702 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4703 if (continuousShadowUpdate) {
4704 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4705 } else {
4706 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4707 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004708 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004709 }
4710 }
4711
Mady Mellor95d743c2017-01-10 12:05:27 -08004712 public void resetExposedMenuView(boolean animate, boolean force) {
4713 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004714 }
4715
4716 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4717 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4718 }
4719
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004720 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004721
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004722 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4723
4724 // ANIMATION_TYPE_ADD
4725 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004726 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004727 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004728 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004729 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004730 .animateZ()
4731 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004732
4733 // ANIMATION_TYPE_REMOVE
4734 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004735 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004736 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004737 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004738 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004739 .animateZ()
4740 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004741
4742 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4743 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004744 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004745 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004746 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004747 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004748 .animateZ()
4749 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004750
4751 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4752 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004753 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004754 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004755 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004756 .animateY()
4757 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004758 .animateZ(),
4759
4760 // ANIMATION_TYPE_START_DRAG
4761 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004762 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004763
4764 // ANIMATION_TYPE_SNAP_BACK
4765 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004766 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004767 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004768
4769 // ANIMATION_TYPE_ACTIVATED_CHILD
4770 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004771 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004772
4773 // ANIMATION_TYPE_DIMMED
4774 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004775 .animateDimmed(),
4776
4777 // ANIMATION_TYPE_CHANGE_POSITION
4778 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004779 .animateAlpha() // maybe the children change positions
4780 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004781 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004782 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004783 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004784 .animateZ(),
4785
4786 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004787 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004788
4789 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4790 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004791 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004792 .animateHeight()
4793 .animateTopInset()
4794 .animateY()
4795 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004796 .animateZ()
4797 .hasDelays(),
4798
4799 // ANIMATION_TYPE_HIDE_SENSITIVE
4800 new AnimationFilter()
4801 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02004802
4803 // ANIMATION_TYPE_VIEW_RESIZE
4804 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004805 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02004806 .animateHeight()
4807 .animateTopInset()
4808 .animateY()
4809 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02004810
Selim Cinekb5605e52015-02-20 18:21:41 +01004811 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
4812 new AnimationFilter()
4813 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004814 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01004815 .animateHeight()
4816 .animateTopInset()
4817 .animateY()
4818 .animateZ(),
4819
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004820 // ANIMATION_TYPE_HEADS_UP_APPEAR
4821 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004822 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004823 .animateHeight()
4824 .animateTopInset()
4825 .animateY()
4826 .animateZ(),
4827
4828 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4829 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004830 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004831 .animateHeight()
4832 .animateTopInset()
4833 .animateY()
4834 .animateZ(),
4835
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004836 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4837 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004838 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004839 .animateHeight()
4840 .animateTopInset()
4841 .animateY()
4842 .animateZ()
4843 .hasDelays(),
4844
Selim Cineka59ecc32015-04-07 10:51:49 -07004845 // ANIMATION_TYPE_HEADS_UP_OTHER
4846 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004847 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07004848 .animateHeight()
4849 .animateTopInset()
4850 .animateY()
4851 .animateZ(),
4852
Selim Cinekd9acca52014-09-01 22:33:25 +02004853 // ANIMATION_TYPE_EVERYTHING
4854 new AnimationFilter()
4855 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004856 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02004857 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02004858 .animateDimmed()
4859 .animateHideSensitive()
4860 .animateHeight()
4861 .animateTopInset()
4862 .animateY()
4863 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004864 };
4865
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004866 static int[] LENGTHS = new int[] {
4867
4868 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004869 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004870
4871 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004872 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004873
4874 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4875 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4876
4877 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4878 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4879
4880 // ANIMATION_TYPE_START_DRAG
4881 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4882
4883 // ANIMATION_TYPE_SNAP_BACK
4884 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4885
4886 // ANIMATION_TYPE_ACTIVATED_CHILD
4887 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
4888
4889 // ANIMATION_TYPE_DIMMED
4890 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004891
4892 // ANIMATION_TYPE_CHANGE_POSITION
4893 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04004894
4895 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004896 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004897
4898 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4899 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02004900
4901 // ANIMATION_TYPE_HIDE_SENSITIVE
4902 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02004903
4904 // ANIMATION_TYPE_VIEW_RESIZE
4905 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02004906
Selim Cinekb5605e52015-02-20 18:21:41 +01004907 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08004908 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01004909
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004910 // ANIMATION_TYPE_HEADS_UP_APPEAR
4911 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
4912
4913 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4914 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4915
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004916 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4917 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4918
Selim Cineka59ecc32015-04-07 10:51:49 -07004919 // ANIMATION_TYPE_HEADS_UP_OTHER
4920 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4921
Selim Cinekd9acca52014-09-01 22:33:25 +02004922 // ANIMATION_TYPE_EVERYTHING
4923 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004924 };
4925
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004926 static final int ANIMATION_TYPE_ADD = 0;
4927 static final int ANIMATION_TYPE_REMOVE = 1;
4928 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
4929 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
4930 static final int ANIMATION_TYPE_START_DRAG = 4;
4931 static final int ANIMATION_TYPE_SNAP_BACK = 5;
4932 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
4933 static final int ANIMATION_TYPE_DIMMED = 7;
4934 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04004935 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004936 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02004937 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02004938 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01004939 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004940 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
4941 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004942 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
4943 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
4944 static final int ANIMATION_TYPE_EVERYTHING = 18;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004945
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004946 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
4947 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
4948
Selim Cinek572bbd42014-04-25 16:43:27 +02004949 final long eventStartTime;
4950 final View changingView;
4951 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004952 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004953 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004954 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004955 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07004956 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02004957
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004958 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004959 this(view, type, LENGTHS[type]);
4960 }
4961
Adrian Roos28f90c72017-05-08 17:24:26 -07004962 AnimationEvent(View view, int type, AnimationFilter filter) {
4963 this(view, type, LENGTHS[type], filter);
4964 }
4965
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004966 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07004967 this(view, type, length, FILTERS[type]);
4968 }
4969
4970 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02004971 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
4972 changingView = view;
4973 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004974 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07004975 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004976 }
4977
4978 /**
4979 * Combines the length of several animation events into a single value.
4980 *
4981 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004982 * @return The combined length. Depending on the event types, this might be the maximum of
4983 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004984 */
4985 static long combineLength(ArrayList<AnimationEvent> events) {
4986 long length = 0;
4987 int size = events.size();
4988 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004989 AnimationEvent event = events.get(i);
4990 length = Math.max(length, event.length);
4991 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
4992 return event.length;
4993 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004994 }
4995 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02004996 }
4997 }
Selim Cinek67b22602014-03-10 15:40:16 +01004998}