blob: 897bb235e7ba7ac18ef4de2b96157954a0acae8f [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 Cinekd35c2792016-01-21 13:20:57 -080032import android.graphics.Color;
Selim Cinek67b22602014-03-10 15:40:16 +010033import android.graphics.Paint;
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;
Selim Cinek67b22602014-03-10 15:40:16 +010040import android.util.AttributeSet;
Selim Cinek972123d2016-05-03 14:25:58 -070041import android.util.FloatProperty;
Selim Cinek67b22602014-03-10 15:40:16 +010042import android.util.Log;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070043import android.util.Pair;
Selim Cinek972123d2016-05-03 14:25:58 -070044import android.util.Property;
Selim Cinek11e33232016-08-05 15:30:53 -070045import android.view.InputDevice;
Selim Cinek67b22602014-03-10 15:40:16 +010046import android.view.MotionEvent;
47import android.view.VelocityTracker;
48import android.view.View;
49import android.view.ViewConfiguration;
50import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020051import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070052import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070053import android.view.accessibility.AccessibilityEvent;
54import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020055import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080056import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010057import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070058import android.widget.ScrollView;
Mady Mellora41587b2016-02-11 18:43:06 -080059import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010060import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Selim Cinek67b22602014-03-10 15:40:16 +010061import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080062import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010063import com.android.systemui.R;
64import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070065import com.android.systemui.classifier.FalsingManager;
Mady Mellor43c2cd12016-12-12 21:05:13 -080066import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
67import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
Selim Cineka32ab602014-06-11 15:06:01 +020068import com.android.systemui.statusbar.ActivatableNotificationView;
Dan Sandlereceda3d2014-07-21 15:35:01 -040069import com.android.systemui.statusbar.DismissView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020070import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010071import com.android.systemui.statusbar.ExpandableNotificationRow;
Jorim Jaggibe565df2014-04-28 17:51:23 +020072import com.android.systemui.statusbar.ExpandableView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070073import com.android.systemui.statusbar.NotificationGuts;
Mady Mellor761cde12017-01-10 11:36:39 -080074import com.android.systemui.statusbar.NotificationMenuRow;
Selim Cinek0cfbef42016-11-09 19:06:36 -080075import com.android.systemui.statusbar.NotificationShelf;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010076import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020077import com.android.systemui.statusbar.StatusBarState;
Selim Cinek33223572016-02-19 19:32:22 -080078import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080079import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080080import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
Selim Cinekb5605e52015-02-20 18:21:41 +010081import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050082import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinekaac93252015-04-14 20:04:12 -070083import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070084import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010085import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +010086
Selim Cinek572bbd42014-04-25 16:43:27 +020087import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -080088import java.util.Collections;
89import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +020090import java.util.HashSet;
Selim Cinek572bbd42014-04-25 16:43:27 +020091
Selim Cinek67b22602014-03-10 15:40:16 +010092/**
93 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
94 */
95public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +020096 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -080097 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Mady Mellor43c2cd12016-12-12 21:05:13 -080098 NotificationMenuRowProvider.OnMenuClickListener, ScrollContainer,
99 VisibilityLocationProvider {
Selim Cinek67b22602014-03-10 15:40:16 +0100100
Selim Cinekd35c2792016-01-21 13:20:57 -0800101 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800102 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100103 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200104 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
105 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200106 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100107 /**
108 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
109 */
110 private static final int INVALID_POINTER = -1;
111
Selim Cinek1408eb52014-06-02 14:45:38 +0200112 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800113 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200114 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100115 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800116 private final Paint mBackgroundPaint = new Paint();
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800117 private boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100118
Selim Cinekbc243a92016-09-27 16:35:13 -0700119 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100120 private int mOwnScrollY;
121 private int mMaxLayoutHeight;
122
123 private VelocityTracker mVelocityTracker;
124 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100125 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100126 private int mTouchSlop;
127 private int mMinimumVelocity;
128 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100129 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200130 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100131 private boolean mIsBeingDragged;
132 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200133 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700134 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100135 private boolean mTouchIsClick;
136 private float mInitialTouchX;
137 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100138
Selim Cinek67b22602014-03-10 15:40:16 +0100139 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100140 private int mContentHeight;
141 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100142 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800143 private int mIncreasedPaddingBetweenElements;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200144 private int mTopPadding;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700145 private int mBottomInset = 0;
Selim Cinek67b22602014-03-10 15:40:16 +0100146
147 /**
148 * The algorithm which calculates the properties for our children
149 */
Muyuan Li87798022016-04-07 17:51:25 -0700150 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100151
152 /**
153 * The current State this Layout is in
154 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200155 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700156 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100157 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800158 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700159 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
160 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
161 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
162 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
163 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200164 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700165 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
166 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200167 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200168 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200169 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800170 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200171
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200172 /**
173 * The raw amount of the overScroll on the top, which is not rubber-banded.
174 */
175 private float mOverScrolledTopPixels;
176
177 /**
178 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
179 */
180 private float mOverScrolledBottomPixels;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200181 private OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200182 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200183 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100184 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200185 private boolean mNeedsAnimation;
186 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200187 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200188 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400189 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100190 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200191 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200192 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200193 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200194 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200195 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700196 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200197 private boolean mExpandingNotification;
198 private boolean mExpandedInThisMotion;
Muyuan Li84b45612016-06-01 11:05:08 -0700199 protected boolean mScrollingEnabled;
Muyuan Li8cf63dc2016-05-18 17:40:58 -0700200 protected DismissView mDismissView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700201 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400202 private boolean mDismissAllInProgress;
Selim Cinek1408eb52014-06-02 14:45:38 +0200203
204 /**
205 * Was the scroller scrolled to the top when the down motion was observed?
206 */
207 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200208 /**
209 * The minimal amount of over scroll which is needed in order to switch to the quick settings
210 * when over scrolling on a expanded card.
211 */
212 private float mMinTopOverScrollToEscape;
213 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700214 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200215 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200216 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700217 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200218 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100219 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700220 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700221
Selim Cinek1408eb52014-06-02 14:45:38 +0200222 /**
223 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
224 * This is needed to avoid scrolling too far after the notification was collapsed in the same
225 * motion.
226 */
227 private int mMaxScrollAfterExpand;
Dan Sandler4247a5c2014-07-23 15:58:08 -0400228 private SwipeHelper.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800229
Mady Mellor761cde12017-01-10 11:36:39 -0800230 private NotificationMenuRow mCurrIconRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800231 private View mTranslatingParentView;
232 private View mGearExposedView;
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800233 private boolean mShouldShowGear;
Selim Cinek1408eb52014-06-02 14:45:38 +0200234
235 /**
236 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
237 * animating.
238 */
239 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700240 private boolean mDisallowDismissInThisMotion;
Jorim Jaggi56306252014-07-03 00:40:09 +0200241 private boolean mInterceptDelegateEnabled;
242 private boolean mDelegateToScrollView;
Selim Cineka59ecc32015-04-07 10:51:49 -0700243 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700244 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200245 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200246 = new ViewTreeObserver.OnPreDrawListener() {
247 @Override
248 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400249 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200250 updateChildren();
251 mChildrenUpdateRequested = false;
252 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200253 return true;
254 }
255 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500256 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100257 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100258 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700259 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek0fccc722015-07-29 17:04:36 -0700260 private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700261 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
262 = new HashSet<>();
263 private HeadsUpManager mHeadsUpManager;
264 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700265 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200266 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700267 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700268 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800269 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700270 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800271 = new ViewTreeObserver.OnPreDrawListener() {
272 @Override
273 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700274 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800275 return true;
276 }
277 };
278 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800279 private Rect mStartAnimationRect = new Rect();
280 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800281 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800282 private boolean mAnimateNextBackgroundBottom;
283 private boolean mAnimateNextBackgroundTop;
284 private ObjectAnimator mBottomAnimator = null;
285 private ObjectAnimator mTopAnimator = null;
286 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
287 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800288 private int mBgColor;
289 private float mDimAmount;
290 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800291 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800292 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
293 @Override
294 public void onAnimationEnd(Animator animation) {
295 mDimAnimator = null;
296 }
297 };
298 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
299 = new ValueAnimator.AnimatorUpdateListener() {
300
301 @Override
302 public void onAnimationUpdate(ValueAnimator animation) {
303 setDimAmount((Float) animation.getAnimatedValue());
304 }
305 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700306 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800307 private boolean mContinuousShadowUpdate;
308 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
309 = new ViewTreeObserver.OnPreDrawListener() {
310
311 @Override
312 public boolean onPreDraw() {
313 updateViewShadows();
314 return true;
315 }
316 };
317 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
318 @Override
319 public int compare(ExpandableView view, ExpandableView otherView) {
320 float endY = view.getTranslationY() + view.getActualHeight();
321 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
322 if (endY < otherEndY) {
323 return -1;
324 } else if (endY > otherEndY) {
325 return 1;
326 } else {
327 // The two notifications end at the same location
328 return 0;
329 }
330 }
331 };
Selim Cinek25503252016-03-03 15:31:43 -0800332 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
Selim Cinekcd5b22f2016-03-08 16:15:41 -0800333 private boolean mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700334 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700335 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800336 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700337 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700338 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400339 private View mForcedScroll;
Selim Cinek972123d2016-05-03 14:25:58 -0700340 private float mBackgroundFadeAmount = 1.0f;
341 private static final Property<NotificationStackScrollLayout, Float> BACKGROUND_FADE =
342 new FloatProperty<NotificationStackScrollLayout>("backgroundFade") {
343 @Override
344 public void setValue(NotificationStackScrollLayout object, float value) {
345 object.setBackgroundFadeAmount(value);
346 }
347
348 @Override
349 public Float get(NotificationStackScrollLayout object) {
350 return object.getBackgroundFadeAmount();
351 }
352 };
Selim Cinekbc243a92016-09-27 16:35:13 -0700353 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700354 private boolean mForwardScrollable;
355 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700356 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700357 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800358 private int mStatusBarHeight;
359 private boolean mNoAmbient;
360 private final Rect mClipRect = new Rect();
361 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700362 private Rect mRequestedClipBounds;
363 private boolean mInHeadsUpPinnedMode;
364 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800365 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100366 private int mCachedBackgroundColor;
Selim Cinek67b22602014-03-10 15:40:16 +0100367
368 public NotificationStackScrollLayout(Context context) {
369 this(context, null);
370 }
371
372 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
373 this(context, attrs, 0);
374 }
375
376 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
377 this(context, attrs, defStyleAttr, 0);
378 }
379
380 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
381 int defStyleRes) {
382 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800383 Resources res = getResources();
384
Selim Cinek281c2022016-10-13 19:14:43 -0700385 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800386 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800387 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
388 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200389 mExpandHelper = new ExpandHelper(getContext(), this,
390 minHeight, maxHeight);
391 mExpandHelper.setEventSource(this);
392 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800393 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Selim Cinek1cf41c12014-08-12 20:06:19 +0200394 mSwipeHelper.setLongPressListener(mLongPressListener);
Muyuan Li333a4fc2016-04-16 17:13:46 -0700395 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100396 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800397 mFalsingManager = FalsingManager.getInstance(context);
398 mShouldShowGear = res.getBoolean(R.bool.config_showNotificationGear);
399 mShouldDrawNotificationBackground =
400 res.getBoolean(R.bool.config_drawNotificationBackground);
401
402 updateWillNotDraw();
Selim Cinek67b22602014-03-10 15:40:16 +0100403 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100404 mDebugPaint = new Paint();
405 mDebugPaint.setColor(0xffff0000);
406 mDebugPaint.setStrokeWidth(2);
407 mDebugPaint.setStyle(Paint.Style.STROKE);
408 }
409 }
410
411 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800412 public void onMenuClicked(View view, int x, int y, MenuItem item) {
413 if (mLongPressListener == null) {
414 return;
415 }
416 if (view instanceof ExpandableNotificationRow) {
417 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800418 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
419 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800420 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800421 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800422 }
423
424 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800425 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700426 if (mTranslatingParentView != null && row == mTranslatingParentView) {
427 mSwipeHelper.setSnappedToGear(false);
428 mGearExposedView = null;
429 mTranslatingParentView = null;
430 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000431 }
432
433 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100434 protected void onDraw(Canvas canvas) {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800435 if (mShouldDrawNotificationBackground && mCurrentBounds.top < mCurrentBounds.bottom) {
Selim Cinekd381bc32016-08-15 12:40:57 -0700436 canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom,
437 mBackgroundPaint);
438 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800439
Selim Cinek67b22602014-03-10 15:40:16 +0100440 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800441 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100442 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800443 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100444 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200445 y = getHeight() - getEmptyBottomMargin();
446 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100447 }
448 }
449
Selim Cinekd35c2792016-01-21 13:20:57 -0800450 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800451 // No need to update the background color if it's not being drawn.
452 if (!mShouldDrawNotificationBackground) {
453 return;
454 }
455
Selim Cinekd35c2792016-01-21 13:20:57 -0800456 float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Selim Cinek972123d2016-05-03 14:25:58 -0700457 alpha *= mBackgroundFadeAmount;
Selim Cinekd35c2792016-01-21 13:20:57 -0800458 // We need to manually blend in the background color
459 int scrimColor = mScrimController.getScrimBehindColor();
460 // SRC_OVER blending Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc
461 float alphaInv = 1 - alpha;
462 int color = Color.argb((int) (alpha * 255 + alphaInv * Color.alpha(scrimColor)),
Selim Cinek972123d2016-05-03 14:25:58 -0700463 (int) (mBackgroundFadeAmount * Color.red(mBgColor)
464 + alphaInv * Color.red(scrimColor)),
465 (int) (mBackgroundFadeAmount * Color.green(mBgColor)
466 + alphaInv * Color.green(scrimColor)),
467 (int) (mBackgroundFadeAmount * Color.blue(mBgColor)
468 + alphaInv * Color.blue(scrimColor)));
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100469 if (mCachedBackgroundColor != color) {
470 mCachedBackgroundColor = color;
471 mBackgroundPaint.setColor(color);
472 invalidate();
473 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800474 }
475
Selim Cinek67b22602014-03-10 15:40:16 +0100476 private void initView(Context context) {
477 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100478 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200479 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100480 final ViewConfiguration configuration = ViewConfiguration.get(context);
481 mTouchSlop = configuration.getScaledTouchSlop();
482 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
483 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100484 mOverflingDistance = configuration.getScaledOverflingDistance();
Selim Cinek67b22602014-03-10 15:40:16 +0100485 mCollapsedSize = context.getResources()
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200486 .getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800487 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700488 mAmbientState.reload(context);
Selim Cinek61633a82016-01-25 15:54:10 -0800489 mPaddingBetweenElements = Math.max(1, context.getResources()
Selim Cinekcacc6042016-01-21 16:16:41 -0800490 .getDimensionPixelSize(R.dimen.notification_divider_height));
Selim Cinek61633a82016-01-25 15:54:10 -0800491 mIncreasedPaddingBetweenElements = context.getResources()
492 .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
Selim Cinek1408eb52014-06-02 14:45:38 +0200493 mMinTopOverScrollToEscape = getResources().getDimensionPixelSize(
494 R.dimen.min_top_overscroll_to_qs);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800495 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cineka5eaa602014-05-12 21:27:47 +0200496 }
497
Selim Cinek25503252016-03-03 15:31:43 -0800498 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700499 mDrawBackgroundAsSrc = asSrc;
500 updateSrcDrawing();
501 }
502
503 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800504 if (!mShouldDrawNotificationBackground) {
505 return;
506 }
507
Selim Cinek48ff9b42016-11-09 19:31:51 -0800508 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700509 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800510 invalidate();
511 }
512
Selim Cinekaef92ef2014-06-06 18:06:04 +0200513 private void notifyHeightChangeListener(ExpandableView view) {
514 if (mOnHeightChangedListener != null) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100515 mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200516 }
Selim Cinek67b22602014-03-10 15:40:16 +0100517 }
518
519 @Override
520 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
521 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400522 // We need to measure all children even the GONE ones, such that the heights are calculated
523 // correctly as they are used to calculate how many we can fit on the screen.
524 final int size = getChildCount();
525 for (int i = 0; i < size; i++) {
526 measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
527 }
Selim Cinek67b22602014-03-10 15:40:16 +0100528 }
529
530 @Override
531 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100532 // we layout all our children centered on the top
533 float centerX = getWidth() / 2.0f;
534 for (int i = 0; i < getChildCount(); i++) {
535 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400536 // We need to layout all children even the GONE ones, such that the heights are
537 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100538 float width = child.getMeasuredWidth();
539 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100540 child.layout((int) (centerX - width / 2.0f),
541 0,
542 (int) (centerX + width / 2.0f),
543 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100544 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200545 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100546 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200547 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200548 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800549 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700550 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100551 }
552
Selim Cinek5bc852a2015-12-21 12:19:09 -0800553 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
554 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200555 mNeedViewResizeAnimation = true;
556 mNeedsAnimation = true;
557 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200558 }
559
Selim Cinekdb167372016-11-17 15:41:17 -0800560 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
561 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800562 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200563 }
564
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200565 public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) {
566 mListener = listener;
567 }
568
Selim Cineka7d4f822016-12-06 14:34:47 -0800569 @Override
570 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
571 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200572 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800573 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200574 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800575 if ((childViewState.location &= ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
576 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100577 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800578 if (row.getVisibility() != View.VISIBLE) {
579 return false;
580 }
581 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200582 }
583
Selim Cinek67b22602014-03-10 15:40:16 +0100584 private void setMaxLayoutHeight(int maxLayoutHeight) {
585 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700586 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200587 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100588 }
589
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200590 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700591 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700592 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700593 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100594 }
595
Selim Cinekbc243a92016-09-27 16:35:13 -0700596 private void updateAlgorithmLayoutMinHeight() {
597 mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
598 }
599
Selim Cinek67b22602014-03-10 15:40:16 +0100600 /**
601 * Updates the children views according to the stack scroll algorithm. Call this whenever
602 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
603 */
604 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800605 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800606 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
607 ? 0
608 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200609 mAmbientState.setScrollY(mOwnScrollY);
610 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200611 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200612 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100613 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200614 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100615 }
616 }
617
Selim Cinekc383fd02016-10-21 15:31:26 -0700618 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700619 mShelf.updateAppearance();
Selim Cinekc383fd02016-10-21 15:31:26 -0700620 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
621 updateBackground();
622 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700623 }
624
Selim Cinek3776fe02016-02-04 13:32:43 -0800625 private void updateScrollStateForAddedChildren() {
626 if (mChildrenToAddAnimated.isEmpty()) {
627 return;
628 }
629 for (int i = 0; i < getChildCount(); i++) {
630 ExpandableView child = (ExpandableView) getChildAt(i);
631 if (mChildrenToAddAnimated.contains(child)) {
632 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800633 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
634 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
635 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800636 int childHeight = getIntrinsicHeight(child) + padding;
637 if (startingPosition < mOwnScrollY) {
638 // This child starts off screen, so let's keep it offscreen to keep the others visible
639
Selim Cinekef406062016-09-29 17:33:13 -0700640 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800641 }
642 }
643 }
644 clampScrollPosition();
645 }
646
Adrian Roos181385c2016-05-05 17:45:44 -0400647 private void updateForcedScroll() {
648 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
649 || !mForcedScroll.isAttachedToWindow())) {
650 mForcedScroll = null;
651 }
652 if (mForcedScroll != null) {
653 ExpandableView expandableView = (ExpandableView) mForcedScroll;
654 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
655 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700656 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400657
658 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700659
660 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
661 // that it is not visible anymore.
662 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700663 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400664 }
665 }
666 }
667
Selim Cinek319bdc42014-05-01 23:01:58 +0200668 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200669 if (!mChildrenUpdateRequested) {
670 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
671 mChildrenUpdateRequested = true;
672 invalidate();
673 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200674 }
675
Selim Cinek67b22602014-03-10 15:40:16 +0100676 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200677 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100678 }
679
Selim Cinekf7a14c02014-07-07 14:01:46 +0200680 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100681 int scrollRange = getScrollRange();
682 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700683 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100684 }
685 }
686
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200687 public int getTopPadding() {
688 return mTopPadding;
689 }
690
Selim Cinek1408eb52014-06-02 14:45:38 +0200691 private void setTopPadding(int topPadding, boolean animate) {
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200692 if (mTopPadding != topPadding) {
693 mTopPadding = topPadding;
694 updateAlgorithmHeightAndPadding();
695 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200696 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200697 mTopPaddingNeedsAnimation = true;
698 mNeedsAnimation = true;
699 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200700 requestChildrenUpdate();
Selim Cinekaef92ef2014-06-06 18:06:04 +0200701 notifyHeightChangeListener(null);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200702 }
703 }
704
705 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700706 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200707 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700708 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200709 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700710 public void setExpandedHeight(float height) {
711 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700712 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800713 int minExpansionHeight = getMinExpansionHeight();
714 if (height < minExpansionHeight) {
715 mClipRect.left = 0;
716 mClipRect.right = getWidth();
717 mClipRect.top = 0;
718 mClipRect.bottom = (int) height;
719 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700720 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800721 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700722 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800723 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200724 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700725 float translationY;
726 float appearEndPosition = getAppearEndPosition();
727 float appearStartPosition = getAppearStartPosition();
728 if (height >= appearEndPosition) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700729 translationY = 0;
Selim Cinek94c2d822016-07-13 18:50:04 -0700730 stackHeight = (int) height;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200731 } else {
Selim Cinek94c2d822016-07-13 18:50:04 -0700732 float appearFraction = getAppearFraction(height);
733 if (appearFraction >= 0) {
734 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
735 appearFraction);
736 } else {
737 // This may happen when pushing up a heads up. We linearly push it up from the
738 // start
739 translationY = height - appearStartPosition + getExpandTranslationStart();
740 }
741 stackHeight = (int) (height - translationY);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200742 }
743 if (stackHeight != mCurrentStackHeight) {
744 mCurrentStackHeight = stackHeight;
745 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200746 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200747 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700748 setStackTranslation(translationY);
Selim Cinekcafa87f2016-10-26 17:00:17 -0700749 }
750
751 private void setRequestedClipBounds(Rect clipRect) {
752 mRequestedClipBounds = clipRect;
753 updateClipping();
754 }
755
756 public void updateClipping() {
757 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
758 && !mHeadsUpAnimatingAway;
759 if (mIsClipped != clipped) {
760 mIsClipped = clipped;
761 updateFadingState();
762 }
763 if (clipped) {
764 setClipBounds(mRequestedClipBounds);
765 } else {
766 setClipBounds(null);
767 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700768 }
769
770 /**
771 * @return The translation at the beginning when expanding.
772 * Measured relative to the resting position.
773 */
774 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800775 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700776 }
777
778 /**
779 * @return the position from where the appear transition starts when expanding.
780 * Measured in absolute height.
781 */
782 private float getAppearStartPosition() {
Selim Cinekd127d792016-11-01 19:11:41 -0700783 if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
784 if (mFirstVisibleBackgroundChild.isAboveShelf()) {
785 // If we ever expanded beyond the first notification, it's allowed to merge into
786 // the shelf
787 return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
788 }
789 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800790 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700791 }
792
793 /**
794 * @return the position from where the appear transition ends when expanding.
795 * Measured in absolute height.
796 */
797 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -0700798 int appearPosition;
Selim Cinekcde90e52016-12-22 21:01:49 +0100799 if (mEmptyShadeView.getVisibility() == GONE) {
800 int minNotificationsForShelf = 1;
801 if (mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) {
802 appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
803 minNotificationsForShelf = 2;
804 } else {
805 appearPosition = 0;
806 }
807 if (getNotGoneChildCount() >= minNotificationsForShelf) {
808 appearPosition += mShelf.getIntrinsicHeight();
809 }
Selim Cinekaa417da2016-10-27 18:17:08 -0700810 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +0100811 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -0700812 }
813 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -0700814 }
815
816 /**
817 * @param height the height of the panel
818 * @return the fraction of the appear animation that has been performed
819 */
820 public float getAppearFraction(float height) {
821 float appearEndPosition = getAppearEndPosition();
822 float appearStartPosition = getAppearStartPosition();
823 return (height - appearStartPosition)
824 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700825 }
826
Selim Cinekd2281152015-04-10 14:37:46 -0700827 public float getStackTranslation() {
828 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700829 }
830
Selim Cinekd2281152015-04-10 14:37:46 -0700831 private void setStackTranslation(float stackTranslation) {
832 if (stackTranslation != mStackTranslation) {
833 mStackTranslation = stackTranslation;
834 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700835 requestChildrenUpdate();
836 }
Selim Cinek67b22602014-03-10 15:40:16 +0100837 }
838
839 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100840 * 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 +0100841 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
842 *
843 * @return either the layout height or the externally defined height, whichever is smaller
844 */
Selim Cinek343e6e22014-04-11 21:23:30 +0200845 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +0100846 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
847 }
848
Selim Cinek816c8e42015-11-19 12:00:45 -0800849 public int getFirstItemMinHeight() {
850 final ExpandableView firstChild = getFirstChildNotGone();
851 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100852 }
853
Dan Sandler4247a5c2014-07-23 15:58:08 -0400854 public void setLongPressListener(SwipeHelper.LongPressListener listener) {
Selim Cinek67b22602014-03-10 15:40:16 +0100855 mSwipeHelper.setLongPressListener(listener);
Dan Sandler4247a5c2014-07-23 15:58:08 -0400856 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +0100857 }
858
Jason Monk16ac3772016-02-10 15:39:21 -0500859 public void setQsContainer(ViewGroup qsContainer) {
860 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +0200861 }
862
Mady Mellorb0a82462016-04-30 17:31:02 -0700863 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100864 public void onChildDismissed(View v) {
Selim Cinekd1395642016-04-28 12:22:42 -0700865 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
866 if (!row.isDismissed()) {
867 handleChildDismissed(v);
868 }
869 ViewGroup transientContainer = row.getTransientContainer();
870 if (transientContainer != null) {
871 transientContainer.removeTransientView(v);
872 }
873 }
874
875 private void handleChildDismissed(View v) {
Dan Sandlereceda3d2014-07-21 15:35:01 -0400876 if (mDismissAllInProgress) {
877 return;
878 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100879 setSwipingInProgress(false);
Selim Cinekeb973562014-05-02 17:07:49 +0200880 if (mDragAnimPendingChildren.contains(v)) {
881 // We start the swipe and finish it in the same frame, we don't want any animation
882 // for the drag
883 mDragAnimPendingChildren.remove(v);
884 }
Selim Cinek572bbd42014-04-25 16:43:27 +0200885 mSwipedOutViews.add(v);
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200886 mAmbientState.onDragFinished(v);
Selim Cinek33223572016-02-19 19:32:22 -0800887 updateContinuousShadowDrawing();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700888 if (v instanceof ExpandableNotificationRow) {
889 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
890 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -0700891 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700892 }
893 }
Selim Cineke9bad242016-06-15 11:46:37 -0700894 performDismiss(v, mGroupManager, false /* fromAccessibility */);
Blazej Magnowski72323322015-07-24 11:49:40 -0700895
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700896 mFalsingManager.onNotificationDismissed();
897 if (mFalsingManager.shouldEnforceBouncer()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500898 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
Felipe Lemeee5d6302016-04-22 16:11:19 -0700899 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -0700900 }
Selim Cinekeb973562014-05-02 17:07:49 +0200901 }
902
Selim Cineke9bad242016-06-15 11:46:37 -0700903 public static void performDismiss(View v, NotificationGroupManager groupManager,
904 boolean fromAccessibility) {
Selim Cinek9e624e72016-07-20 13:46:49 -0700905 if (!(v instanceof ExpandableNotificationRow)) {
906 return;
Selim Cinek2a739342016-03-17 10:28:55 -0700907 }
Selim Cinek9e624e72016-07-20 13:46:49 -0700908 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
909 if (groupManager.isOnlyChildInGroup(row.getStatusBarNotification())) {
910 ExpandableNotificationRow groupSummary =
911 groupManager.getLogicalGroupSummary(row.getStatusBarNotification());
912 if (groupSummary.isClearable()) {
913 performDismiss(groupSummary, groupManager, fromAccessibility);
914 }
915 }
916 row.setDismissed(true, fromAccessibility);
917 if (row.isClearable()) {
918 row.performDismiss();
Selim Cinek2a739342016-03-17 10:28:55 -0700919 }
920 if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
921 }
922
Selim Cinekeb973562014-05-02 17:07:49 +0200923 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -0800924 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200925 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -0800926 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +0200927 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +0200928 if (mAnimationsEnabled) {
929 mSnappedBackChildren.add(animView);
930 mNeedsAnimation = true;
931 }
Selim Cinekeb973562014-05-02 17:07:49 +0200932 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +0200933 } else {
934 // We start the swipe and snap back in the same frame, we don't want any animation
935 mDragAnimPendingChildren.remove(animView);
936 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700937 if (mCurrIconRow != null && targetLeft == 0) {
Mady Mellor43c2cd12016-12-12 21:05:13 -0800938 mCurrIconRow.resetState(true /* notify */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700939 mCurrIconRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -0800940 }
Selim Cinek67b22602014-03-10 15:40:16 +0100941 }
942
Adrian Roos5d9cc662014-05-28 17:08:13 +0200943 @Override
944 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Selim Cinek131c1e22015-05-11 19:04:49 -0700945 if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
Selim Cinekaac93252015-04-14 20:04:12 -0700946 mScrimController.setTopHeadsUpDragAmount(animView,
Winsonbde852d2016-04-15 19:06:54 -0700947 Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
Selim Cinekaac93252015-04-14 20:04:12 -0700948 }
Mady Mellor4b80b102016-01-22 08:03:58 -0800949 return true; // Don't fade out the notification
Adrian Roos5d9cc662014-05-28 17:08:13 +0200950 }
951
Mady Mellorb0a82462016-04-30 17:31:02 -0700952 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100953 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700954 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100955 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200956 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -0800957 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -0700958 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +0200959 mDragAnimPendingChildren.add(v);
960 mNeedsAnimation = true;
961 }
Selim Cinekeb973562014-05-02 17:07:49 +0200962 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +0100963 }
964
Selim Cinek684a4422015-04-15 16:18:39 -0700965 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -0700966 if (v instanceof ExpandableNotificationRow) {
967 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -0700968 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -0700969 }
970 return false;
971 }
972
973 private boolean isHeadsUp(View v) {
974 if (v instanceof ExpandableNotificationRow) {
975 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
976 return row.isHeadsUp();
977 }
978 return false;
979 }
980
Mady Mellorb0a82462016-04-30 17:31:02 -0700981 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100982 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700983 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100984 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100985 }
986
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700987 @Override
988 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500989 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700990 }
991
Mady Mellor4b80b102016-01-22 08:03:58 -0800992 @Override
Selim Cinek67b22602014-03-10 15:40:16 +0100993 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -0800994 View child = getChildAtPosition(ev.getX(), ev.getY());
995 if (child instanceof ExpandableNotificationRow) {
996 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
997 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -0800998 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -0700999 && (parent.areGutsExposed()
1000 || mGearExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001001 || (parent.getNotificationChildren().size() == 1
1002 && parent.isClearable()))) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001003 // In this case the group is expanded and showing the gear for the
1004 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001005 // child notifications so we use the parent of the child. We also do the same
1006 // if we only have a single child.
1007 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001008 }
1009 }
1010 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001011 }
1012
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001013 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1014 getLocationOnScreen(mTempInt2);
1015 float localTouchY = touchY - mTempInt2[1];
1016
1017 ExpandableView closestChild = null;
1018 float minDist = Float.MAX_VALUE;
1019
1020 // find the view closest to the location, accounting for GONE views
1021 final int count = getChildCount();
1022 for (int childIdx = 0; childIdx < count; childIdx++) {
1023 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1024 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001025 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001026 continue;
1027 }
1028 float childTop = slidingChild.getTranslationY();
1029 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001030 float bottom = childTop + slidingChild.getActualHeight()
1031 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001032
1033 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1034 if (dist < minDist) {
1035 closestChild = slidingChild;
1036 minDist = dist;
1037 }
1038 }
1039 return closestChild;
1040 }
1041
Mady Mellorb0a82462016-04-30 17:31:02 -07001042 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001043 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001044 getLocationOnScreen(mTempInt2);
1045 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001046 }
1047
Mady Mellorb0a82462016-04-30 17:31:02 -07001048 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001049 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001050 // find the view under the pointer, accounting for GONE views
1051 final int count = getChildCount();
1052 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001053 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001054 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001055 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001056 continue;
1057 }
Selim Cinek89faff12014-06-19 16:29:04 -07001058 float childTop = slidingChild.getTranslationY();
1059 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001060 float bottom = childTop + slidingChild.getActualHeight()
1061 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001062
1063 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1064 // camera affordance).
1065 int left = 0;
1066 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001067
1068 if (touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001069 if (slidingChild instanceof ExpandableNotificationRow) {
1070 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001071 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Selim Cinek5bc852a2015-12-21 12:19:09 -08001072 && mHeadsUpManager.getTopEntry().entry.row != row
1073 && mGroupManager.getGroupSummary(
1074 mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
1075 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001076 continue;
1077 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001078 return row.getViewAtPosition(touchY - childTop);
1079 }
Selim Cinek67b22602014-03-10 15:40:16 +01001080 return slidingChild;
1081 }
1082 }
1083 return null;
1084 }
1085
Mady Mellorb0a82462016-04-30 17:31:02 -07001086 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001087 public boolean canChildBeExpanded(View v) {
1088 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001089 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001090 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001091 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001092 }
1093
Chris Wren6abeeb92016-05-26 14:44:38 -04001094 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001095 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001096 public void setUserExpandedChild(View v, boolean userExpanded) {
1097 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001098 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001099 if (userExpanded && onKeyguard()) {
1100 // Due to a race when locking the screen while touching, a notification may be
1101 // expanded even after we went back to keyguard. An example of this happens if
1102 // you click in the empty space while expanding a group.
1103
1104 // We also need to un-user lock it here, since otherwise the content height
1105 // calculated might be wrong. We also can't invert the two calls since
1106 // un-userlocking it will trigger a layout switch in the content view.
1107 row.setUserLocked(false);
1108 updateContentHeight();
1109 notifyHeightChangeListener(row);
1110 return;
1111 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001112 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1113 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001114 }
1115 }
1116
Mady Mellorb0a82462016-04-30 17:31:02 -07001117 @Override
1118 public void setExpansionCancelled(View v) {
1119 if (v instanceof ExpandableNotificationRow) {
1120 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1121 }
1122 }
1123
1124 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001125 public void setUserLockedChild(View v, boolean userLocked) {
1126 if (v instanceof ExpandableNotificationRow) {
1127 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1128 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001129 removeLongPressCallback();
1130 requestDisallowInterceptTouchEvent(true);
1131 }
1132
1133 @Override
1134 public void expansionStateChanged(boolean isExpanding) {
1135 mExpandingNotification = isExpanding;
1136 if (!mExpandedInThisMotion) {
1137 mMaxScrollAfterExpand = mOwnScrollY;
1138 mExpandedInThisMotion = true;
1139 }
1140 }
1141
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001142 @Override
1143 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001144 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001145 }
1146
Selim Cinek1408eb52014-06-02 14:45:38 +02001147 public void setScrollingEnabled(boolean enable) {
1148 mScrollingEnabled = enable;
1149 }
1150
Adrian Roos181385c2016-05-05 17:45:44 -04001151 @Override
1152 public void lockScrollTo(View v) {
1153 if (mForcedScroll == v) {
1154 return;
1155 }
1156 mForcedScroll = v;
1157 scrollTo(v);
1158 }
1159
1160 @Override
Ricky Waicd35def2016-05-03 11:07:07 +01001161 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001162 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001163 int positionInLinearLayout = getPositionInLinearLayout(v);
1164 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1165 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001166
Adrian Roos4a579672016-05-24 16:54:37 -07001167 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1168 // that it is not visible anymore.
1169 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001170 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1171 mDontReportNextOverScroll = true;
1172 postInvalidateOnAnimation();
Ricky Waicd35def2016-05-03 11:07:07 +01001173 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001174 }
Ricky Waicd35def2016-05-03 11:07:07 +01001175 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001176 }
1177
Adrian Roos181385c2016-05-05 17:45:44 -04001178 /**
1179 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1180 * the IME.
1181 */
1182 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1183 return positionInLinearLayout + v.getIntrinsicHeight() +
1184 getImeInset() - getHeight() + getTopPadding();
1185 }
1186
Adrian Roos5153d4a2016-03-22 10:01:56 -07001187 @Override
1188 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001189 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001190
1191 int range = getScrollRange();
1192 if (mOwnScrollY > range) {
1193 // HACK: We're repeatedly getting staggered insets here while the IME is
1194 // animating away. To work around that we'll wait until things have settled.
1195 removeCallbacks(mReclamp);
1196 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001197 } else if (mForcedScroll != null) {
1198 // The scroll was requested before we got the actual inset - in case we need
1199 // to scroll up some more do so now.
1200 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001201 }
1202 return insets;
1203 }
1204
1205 private Runnable mReclamp = new Runnable() {
1206 @Override
1207 public void run() {
1208 int range = getScrollRange();
1209 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1210 mDontReportNextOverScroll = true;
1211 mDontClampNextScroll = true;
1212 postInvalidateOnAnimation();
1213 }
1214 };
1215
Selim Cinek1408eb52014-06-02 14:45:38 +02001216 public void setExpandingEnabled(boolean enable) {
1217 mExpandHelper.setEnabled(enable);
1218 }
1219
1220 private boolean isScrollingEnabled() {
1221 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001222 }
1223
Mady Mellorb0a82462016-04-30 17:31:02 -07001224 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001225 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001226 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001227 }
1228
Selim Cinek19c8c702014-08-25 22:09:19 +02001229 @Override
1230 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001231 return onKeyguard();
1232 }
1233
1234 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001235 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001236 }
1237
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001238 private void setSwipingInProgress(boolean isSwiped) {
1239 mSwipingInProgress = isSwiped;
1240 if(isSwiped) {
1241 requestDisallowInterceptTouchEvent(true);
1242 }
Selim Cinek67b22602014-03-10 15:40:16 +01001243 }
1244
1245 @Override
1246 protected void onConfigurationChanged(Configuration newConfig) {
1247 super.onConfigurationChanged(newConfig);
1248 float densityScale = getResources().getDisplayMetrics().density;
1249 mSwipeHelper.setDensityScale(densityScale);
1250 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1251 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1252 initView(getContext());
1253 }
1254
Dan Sandlereceda3d2014-07-21 15:35:01 -04001255 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001256 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1257 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001258 }
1259
Mady Mellor86889c22016-04-18 16:37:06 -07001260 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001261 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor86889c22016-04-18 16:37:06 -07001262 // If the child is showing the gear to go to settings, snap to that
1263 float targetLeft = child.getSettingsRow().isVisible() ? child.getTranslation() : 0;
1264 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001265 }
1266
Selim Cinek67b22602014-03-10 15:40:16 +01001267 @Override
1268 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001269 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1270 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001271 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001272 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001273 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001274 if (isCancelOrUp) {
1275 mExpandHelper.onlyObserveMovements(false);
1276 }
1277 boolean wasExpandingBefore = mExpandingNotification;
1278 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001279 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1280 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001281 dispatchDownEventToScroller(ev);
1282 }
1283 }
Selim Cinek67b22602014-03-10 15:40:16 +01001284 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001285 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1286 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001287 scrollerWantsIt = onScrollTouch(ev);
1288 }
1289 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001290 if (!mIsBeingDragged
1291 && !mExpandingNotification
1292 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001293 && !mOnlyScrollingInThisMotion
1294 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001295 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1296 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001297 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1298 }
1299
1300 private void dispatchDownEventToScroller(MotionEvent ev) {
1301 MotionEvent downEvent = MotionEvent.obtain(ev);
1302 downEvent.setAction(MotionEvent.ACTION_DOWN);
1303 onScrollTouch(downEvent);
1304 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001305 }
1306
Selim Cinek11e33232016-08-05 15:30:53 -07001307 @Override
1308 public boolean onGenericMotionEvent(MotionEvent event) {
1309 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1310 || mDisallowScrollingInThisMotion) {
1311 return false;
1312 }
1313 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1314 switch (event.getAction()) {
1315 case MotionEvent.ACTION_SCROLL: {
1316 if (!mIsBeingDragged) {
1317 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1318 if (vscroll != 0) {
1319 final int delta = (int) (vscroll * getVerticalScrollFactor());
1320 final int range = getScrollRange();
1321 int oldScrollY = mOwnScrollY;
1322 int newScrollY = oldScrollY - delta;
1323 if (newScrollY < 0) {
1324 newScrollY = 0;
1325 } else if (newScrollY > range) {
1326 newScrollY = range;
1327 }
1328 if (newScrollY != oldScrollY) {
1329 customScrollTo(newScrollY);
1330 return true;
1331 }
1332 }
1333 }
1334 }
1335 }
1336 }
1337 return super.onGenericMotionEvent(event);
1338 }
1339
Selim Cinek67b22602014-03-10 15:40:16 +01001340 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001341 if (!isScrollingEnabled()) {
1342 return false;
1343 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001344 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001345 return false;
1346 }
Adrian Roos181385c2016-05-05 17:45:44 -04001347 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001348 initVelocityTrackerIfNotExists();
1349 mVelocityTracker.addMovement(ev);
1350
1351 final int action = ev.getAction();
1352
1353 switch (action & MotionEvent.ACTION_MASK) {
1354 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001355 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001356 return false;
1357 }
1358 boolean isBeingDragged = !mScroller.isFinished();
1359 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001360 /*
1361 * If being flinged and user touches, stop the fling. isFinished
1362 * will be false if being flinged.
1363 */
1364 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001365 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001366 }
1367
1368 // Remember where the motion event started
1369 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001370 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001371 mActivePointerId = ev.getPointerId(0);
1372 break;
1373 }
1374 case MotionEvent.ACTION_MOVE:
1375 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1376 if (activePointerIndex == -1) {
1377 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1378 break;
1379 }
1380
1381 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001382 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001383 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001384 final int xDiff = Math.abs(x - mDownX);
1385 final int yDiff = Math.abs(deltaY);
1386 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001387 setIsBeingDragged(true);
1388 if (deltaY > 0) {
1389 deltaY -= mTouchSlop;
1390 } else {
1391 deltaY += mTouchSlop;
1392 }
1393 }
1394 if (mIsBeingDragged) {
1395 // Scroll to follow the motion event
1396 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001397 int range = getScrollRange();
1398 if (mExpandedInThisMotion) {
1399 range = Math.min(range, mMaxScrollAfterExpand);
1400 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001401
1402 float scrollAmount;
1403 if (deltaY < 0) {
1404 scrollAmount = overScrollDown(deltaY);
1405 } else {
1406 scrollAmount = overScrollUp(deltaY, range);
1407 }
Selim Cinek67b22602014-03-10 15:40:16 +01001408
1409 // Calling overScrollBy will call onOverScrolled, which
1410 // calls onScrollChanged if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001411 if (scrollAmount != 0.0f) {
1412 // The scrolling motion could not be compensated with the
1413 // existing overScroll, we have to scroll the view
1414 overScrollBy(0, (int) scrollAmount, 0, mOwnScrollY,
1415 0, range, 0, getHeight() / 2, true);
Selim Cinek67b22602014-03-10 15:40:16 +01001416 }
Selim Cinek67b22602014-03-10 15:40:16 +01001417 }
1418 break;
1419 case MotionEvent.ACTION_UP:
1420 if (mIsBeingDragged) {
1421 final VelocityTracker velocityTracker = mVelocityTracker;
1422 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1423 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1424
Selim Cinek1408eb52014-06-02 14:45:38 +02001425 if (shouldOverScrollFling(initialVelocity)) {
1426 onOverScrollFling(true, initialVelocity);
1427 } else {
1428 if (getChildCount() > 0) {
1429 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1430 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1431 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1432 fling(-initialVelocity);
1433 } else {
1434 onOverScrollFling(false, initialVelocity);
1435 }
1436 } else {
1437 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1438 getScrollRange())) {
1439 postInvalidateOnAnimation();
1440 }
Selim Cinek67b22602014-03-10 15:40:16 +01001441 }
1442 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001443 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001444 mActivePointerId = INVALID_POINTER;
1445 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001446 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001447
Selim Cinek67b22602014-03-10 15:40:16 +01001448 break;
1449 case MotionEvent.ACTION_CANCEL:
1450 if (mIsBeingDragged && getChildCount() > 0) {
1451 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
1452 postInvalidateOnAnimation();
1453 }
1454 mActivePointerId = INVALID_POINTER;
1455 endDrag();
1456 }
1457 break;
1458 case MotionEvent.ACTION_POINTER_DOWN: {
1459 final int index = ev.getActionIndex();
1460 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001461 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001462 mActivePointerId = ev.getPointerId(index);
1463 break;
1464 }
1465 case MotionEvent.ACTION_POINTER_UP:
1466 onSecondaryPointerUp(ev);
1467 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001468 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001469 break;
1470 }
1471 return true;
1472 }
1473
Muyuan Li84b45612016-06-01 11:05:08 -07001474 protected boolean isInsideQsContainer(MotionEvent ev) {
1475 return ev.getY() < mQsContainer.getBottom();
1476 }
1477
Selim Cinek1408eb52014-06-02 14:45:38 +02001478 private void onOverScrollFling(boolean open, int initialVelocity) {
1479 if (mOverscrollTopChangedListener != null) {
1480 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1481 }
1482 mDontReportNextOverScroll = true;
1483 setOverScrollAmount(0.0f, true, false);
1484 }
1485
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001486 /**
1487 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1488 *
1489 * @param deltaY The amount to scroll upwards, has to be positive.
1490 * @return The amount of scrolling to be performed by the scroller,
1491 * not handled by the overScroll amount.
1492 */
1493 private float overScrollUp(int deltaY, int range) {
1494 deltaY = Math.max(deltaY, 0);
1495 float currentTopAmount = getCurrentOverScrollAmount(true);
1496 float newTopAmount = currentTopAmount - deltaY;
1497 if (currentTopAmount > 0) {
1498 setOverScrollAmount(newTopAmount, true /* onTop */,
1499 false /* animate */);
1500 }
1501 // Top overScroll might not grab all scrolling motion,
1502 // we have to scroll as well.
1503 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1504 float newScrollY = mOwnScrollY + scrollAmount;
1505 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001506 if (!mExpandedInThisMotion) {
1507 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1508 // We overScroll on the top
1509 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1510 false /* onTop */,
1511 false /* animate */);
1512 }
Selim Cinekef406062016-09-29 17:33:13 -07001513 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001514 scrollAmount = 0.0f;
1515 }
1516 return scrollAmount;
1517 }
1518
1519 /**
1520 * Perform a scroll downward and adapt the overscroll amounts accordingly
1521 *
1522 * @param deltaY The amount to scroll downwards, has to be negative.
1523 * @return The amount of scrolling to be performed by the scroller,
1524 * not handled by the overScroll amount.
1525 */
1526 private float overScrollDown(int deltaY) {
1527 deltaY = Math.min(deltaY, 0);
1528 float currentBottomAmount = getCurrentOverScrollAmount(false);
1529 float newBottomAmount = currentBottomAmount + deltaY;
1530 if (currentBottomAmount > 0) {
1531 setOverScrollAmount(newBottomAmount, false /* onTop */,
1532 false /* animate */);
1533 }
1534 // Bottom overScroll might not grab all scrolling motion,
1535 // we have to scroll as well.
1536 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1537 float newScrollY = mOwnScrollY + scrollAmount;
1538 if (newScrollY < 0) {
1539 float currentTopPixels = getCurrentOverScrolledPixels(true);
1540 // We overScroll on the top
1541 setOverScrolledPixels(currentTopPixels - newScrollY,
1542 true /* onTop */,
1543 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001544 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001545 scrollAmount = 0.0f;
1546 }
1547 return scrollAmount;
1548 }
1549
Selim Cinek67b22602014-03-10 15:40:16 +01001550 private void onSecondaryPointerUp(MotionEvent ev) {
1551 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1552 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1553 final int pointerId = ev.getPointerId(pointerIndex);
1554 if (pointerId == mActivePointerId) {
1555 // This was our active pointer going up. Choose a new
1556 // active pointer and adjust accordingly.
1557 // TODO: Make this decision more intelligent.
1558 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1559 mLastMotionY = (int) ev.getY(newPointerIndex);
1560 mActivePointerId = ev.getPointerId(newPointerIndex);
1561 if (mVelocityTracker != null) {
1562 mVelocityTracker.clear();
1563 }
1564 }
1565 }
1566
1567 private void initVelocityTrackerIfNotExists() {
1568 if (mVelocityTracker == null) {
1569 mVelocityTracker = VelocityTracker.obtain();
1570 }
1571 }
1572
1573 private void recycleVelocityTracker() {
1574 if (mVelocityTracker != null) {
1575 mVelocityTracker.recycle();
1576 mVelocityTracker = null;
1577 }
1578 }
1579
1580 private void initOrResetVelocityTracker() {
1581 if (mVelocityTracker == null) {
1582 mVelocityTracker = VelocityTracker.obtain();
1583 } else {
1584 mVelocityTracker.clear();
1585 }
1586 }
1587
Ricky Waicd35def2016-05-03 11:07:07 +01001588 public void setFinishScrollingCallback(Runnable runnable) {
1589 mFinishScrollingCallback = runnable;
1590 }
1591
Selim Cinek67b22602014-03-10 15:40:16 +01001592 @Override
1593 public void computeScroll() {
1594 if (mScroller.computeScrollOffset()) {
1595 // This is called at drawing time by ViewGroup.
1596 int oldX = mScrollX;
1597 int oldY = mOwnScrollY;
1598 int x = mScroller.getCurrX();
1599 int y = mScroller.getCurrY();
1600
1601 if (oldX != x || oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001602 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001603 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1604 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001605 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001606 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1607 }
1608 }
Selim Cinek67b22602014-03-10 15:40:16 +01001609
Adrian Roos5153d4a2016-03-22 10:01:56 -07001610 if (mDontClampNextScroll) {
1611 range = Math.max(range, oldY);
1612 }
Selim Cinek67b22602014-03-10 15:40:16 +01001613 overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001614 0, (int) (mMaxOverScroll), false);
Selim Cinek67b22602014-03-10 15:40:16 +01001615 onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
Selim Cinek67b22602014-03-10 15:40:16 +01001616 }
1617
1618 // Keep on drawing until the animation has finished.
1619 postInvalidateOnAnimation();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001620 } else {
1621 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001622 if (mFinishScrollingCallback != null) {
1623 mFinishScrollingCallback.run();
1624 }
Selim Cinek67b22602014-03-10 15:40:16 +01001625 }
1626 }
1627
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001628 @Override
Selim Cinek4195dd02014-05-19 18:16:14 +02001629 protected boolean overScrollBy(int deltaX, int deltaY,
1630 int scrollX, int scrollY,
1631 int scrollRangeX, int scrollRangeY,
1632 int maxOverScrollX, int maxOverScrollY,
1633 boolean isTouchEvent) {
1634
1635 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001636 final int top = -maxOverScrollY;
1637 final int bottom = maxOverScrollY + scrollRangeY;
1638
1639 boolean clampedY = false;
1640 if (newScrollY > bottom) {
1641 newScrollY = bottom;
1642 clampedY = true;
1643 } else if (newScrollY < top) {
1644 newScrollY = top;
1645 clampedY = true;
1646 }
1647
1648 onOverScrolled(0, newScrollY, false, clampedY);
1649
1650 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001651 }
1652
1653 /**
1654 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1655 * overscroll effect based on numPixels. By default this will also cancel animations on the
1656 * same overScroll edge.
1657 *
1658 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1659 * the rubber-banding logic.
1660 * @param onTop Should the effect be applied on top of the scroller.
1661 * @param animate Should an animation be performed.
1662 */
1663 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001664 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001665 }
1666
1667 /**
1668 * Set the effective overScroll amount which will be directly reflected in the layout.
1669 * By default this will also cancel animations on the same overScroll edge.
1670 *
1671 * @param amount The amount to overScroll by.
1672 * @param onTop Should the effect be applied on top of the scroller.
1673 * @param animate Should an animation be performed.
1674 */
1675 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1676 setOverScrollAmount(amount, onTop, animate, true);
1677 }
1678
1679 /**
1680 * Set the effective overScroll amount which will be directly reflected in the layout.
1681 *
1682 * @param amount The amount to overScroll by.
1683 * @param onTop Should the effect be applied on top of the scroller.
1684 * @param animate Should an animation be performed.
1685 * @param cancelAnimators Should running animations be cancelled.
1686 */
1687 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1688 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001689 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1690 }
1691
1692 /**
1693 * Set the effective overScroll amount which will be directly reflected in the layout.
1694 *
1695 * @param amount The amount to overScroll by.
1696 * @param onTop Should the effect be applied on top of the scroller.
1697 * @param animate Should an animation be performed.
1698 * @param cancelAnimators Should running animations be cancelled.
1699 * @param isRubberbanded The value which will be passed to
1700 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1701 */
1702 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1703 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001704 if (cancelAnimators) {
1705 mStateAnimator.cancelOverScrollAnimators(onTop);
1706 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001707 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001708 }
1709
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001710 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1711 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001712 amount = Math.max(0, amount);
1713 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001714 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001715 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001716 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001717 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001718 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001719 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001720 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001721 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001722 }
1723 }
1724
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001725 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001726 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1727 if (mDontReportNextOverScroll) {
1728 mDontReportNextOverScroll = false;
1729 return;
1730 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001731 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001732 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001733 }
1734 }
1735
1736 public void setOverscrollTopChangedListener(
1737 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1738 mOverscrollTopChangedListener = overscrollTopChangedListener;
1739 }
1740
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001741 public float getCurrentOverScrollAmount(boolean top) {
1742 return mAmbientState.getOverScrollAmount(top);
1743 }
1744
1745 public float getCurrentOverScrolledPixels(boolean top) {
1746 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
1747 }
1748
1749 private void setOverScrolledPixels(float amount, boolean onTop) {
1750 if (onTop) {
1751 mOverScrolledTopPixels = amount;
1752 } else {
1753 mOverScrolledBottomPixels = amount;
1754 }
1755 }
1756
Selim Cinek319bdc42014-05-01 23:01:58 +02001757 private void customScrollTo(int y) {
Selim Cinekef406062016-09-29 17:33:13 -07001758 setOwnScrollY(y);
Selim Cinek3af00cf2014-05-07 17:27:26 +02001759 updateChildren();
Selim Cinek67b22602014-03-10 15:40:16 +01001760 }
1761
1762 @Override
Selim Cinek319bdc42014-05-01 23:01:58 +02001763 protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001764 // Treat animating scrolls differently; see #computeScroll() for why.
1765 if (!mScroller.isFinished()) {
1766 final int oldX = mScrollX;
1767 final int oldY = mOwnScrollY;
1768 mScrollX = scrollX;
Selim Cinekef406062016-09-29 17:33:13 -07001769 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001770 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001771 springBack();
1772 } else {
1773 onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
1774 invalidateParentIfNeeded();
1775 updateChildren();
Jorim Jaggi290600a2014-05-30 17:02:20 +02001776 float overScrollTop = getCurrentOverScrollAmount(true);
1777 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001778 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001779 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001780 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001781 }
Selim Cinek67b22602014-03-10 15:40:16 +01001782 }
Selim Cinek67b22602014-03-10 15:40:16 +01001783 } else {
1784 customScrollTo(scrollY);
1785 scrollTo(scrollX, mScrollY);
1786 }
1787 }
1788
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001789 private void springBack() {
1790 int scrollRange = getScrollRange();
1791 boolean overScrolledTop = mOwnScrollY <= 0;
1792 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
1793 if (overScrolledTop || overScrolledBottom) {
1794 boolean onTop;
1795 float newAmount;
1796 if (overScrolledTop) {
1797 onTop = true;
1798 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07001799 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02001800 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001801 } else {
1802 onTop = false;
1803 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07001804 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001805 }
1806 setOverScrollAmount(newAmount, onTop, false);
1807 setOverScrollAmount(0.0f, onTop, true);
1808 mScroller.forceFinished(true);
1809 }
1810 }
1811
Selim Cinek67b22602014-03-10 15:40:16 +01001812 private int getScrollRange() {
Selim Cineka424c502016-04-05 13:07:54 -07001813 int contentHeight = getContentHeight();
Selim Cinekdb167372016-11-17 15:41:17 -08001814 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07001815 int imeInset = getImeInset();
1816 scrollRange += Math.min(imeInset, Math.max(0,
1817 getContentHeight() - (getHeight() - imeInset)));
1818 return scrollRange;
1819 }
1820
1821 private int getImeInset() {
1822 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01001823 }
1824
Selim Cinek343e6e22014-04-11 21:23:30 +02001825 /**
1826 * @return the first child which has visibility unequal to GONE
1827 */
Selim Cinekb55386d2015-12-16 17:26:49 -08001828 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02001829 int childCount = getChildCount();
1830 for (int i = 0; i < childCount; i++) {
1831 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001832 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08001833 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02001834 }
1835 }
1836 return null;
1837 }
1838
Selim Cinek4a1ac842014-05-01 15:51:58 +02001839 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001840 * @return the child before the given view which has visibility unequal to GONE
1841 */
1842 public ExpandableView getViewBeforeView(ExpandableView view) {
1843 ExpandableView previousView = null;
1844 int childCount = getChildCount();
1845 for (int i = 0; i < childCount; i++) {
1846 View child = getChildAt(i);
1847 if (child == view) {
1848 return previousView;
1849 }
1850 if (child.getVisibility() != View.GONE) {
1851 previousView = (ExpandableView) child;
1852 }
1853 }
1854 return null;
1855 }
1856
1857 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001858 * @return The first child which has visibility unequal to GONE which is currently below the
1859 * given translationY or equal to it.
1860 */
1861 private View getFirstChildBelowTranlsationY(float translationY) {
1862 int childCount = getChildCount();
1863 for (int i = 0; i < childCount; i++) {
1864 View child = getChildAt(i);
1865 if (child.getVisibility() != View.GONE && child.getTranslationY() >= translationY) {
1866 return child;
1867 }
1868 }
1869 return null;
1870 }
1871
1872 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02001873 * @return the last child which has visibility unequal to GONE
1874 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001875 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02001876 int childCount = getChildCount();
1877 for (int i = childCount - 1; i >= 0; i--) {
1878 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001879 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02001880 return child;
1881 }
1882 }
1883 return null;
1884 }
1885
Jorim Jaggi069cd032014-05-15 03:09:01 +02001886 /**
1887 * @return the number of children which have visibility unequal to GONE
1888 */
1889 public int getNotGoneChildCount() {
1890 int childCount = getChildCount();
1891 int count = 0;
1892 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07001893 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08001894 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02001895 count++;
1896 }
1897 }
1898 return count;
1899 }
1900
Jorim Jaggi30c305c2014-07-01 23:34:41 +02001901 public int getContentHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001902 return mContentHeight;
1903 }
1904
1905 private void updateContentHeight() {
1906 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08001907 float previousPaddingRequest = mPaddingBetweenElements;
1908 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07001909 int numShownItems = 0;
1910 boolean finish = false;
Adrian Roos7a9551a2017-01-11 12:27:49 -08001911 int maxDisplayedNotifications = mAmbientState.isDark()
1912 ? (mPulsing ? 1 : 0)
1913 : mMaxDisplayedNotifications;
1914
Selim Cinek67b22602014-03-10 15:40:16 +01001915 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08001916 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Selim Cinek281c2022016-10-13 19:14:43 -07001917 if (expandableView.getVisibility() != View.GONE
1918 && !expandableView.hasNoContentHeight()) {
Adrian Roos7a9551a2017-01-11 12:27:49 -08001919 if (maxDisplayedNotifications != -1
1920 && numShownItems >= maxDisplayedNotifications) {
Selim Cinekad7fac02016-10-18 17:09:15 -07001921 expandableView = mShelf;
1922 finish = true;
1923 }
Selim Cinek42357e02016-02-24 18:48:01 -08001924 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08001925 float padding;
1926 if (increasedPaddingAmount >= 0.0f) {
1927 padding = (int) NotificationUtils.interpolate(
1928 previousPaddingRequest,
1929 mIncreasedPaddingBetweenElements,
1930 increasedPaddingAmount);
1931 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08001932 mPaddingBetweenElements,
1933 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08001934 increasedPaddingAmount);
1935 } else {
1936 int ownPadding = (int) NotificationUtils.interpolate(
1937 0,
1938 mPaddingBetweenElements,
1939 1.0f + increasedPaddingAmount);
1940 if (previousPaddingAmount > 0.0f) {
1941 padding = (int) NotificationUtils.interpolate(
1942 ownPadding,
1943 mIncreasedPaddingBetweenElements,
1944 previousPaddingAmount);
1945 } else {
1946 padding = ownPadding;
1947 }
1948 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02001949 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08001950 if (height != 0) {
1951 height += padding;
1952 }
1953 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08001954 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07001955 numShownItems++;
1956 if (finish) {
1957 break;
1958 }
Selim Cinek67b22602014-03-10 15:40:16 +01001959 }
1960 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001961 mContentHeight = height + mTopPadding;
Selim Cinekc22fff62016-05-20 12:44:30 -07001962 updateScrollability();
Selim Cinek91d4cba2016-11-10 19:59:48 -08001963 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07001964 }
1965
1966 private void updateScrollability() {
1967 boolean scrollable = getScrollRange() > 0;
1968 if (scrollable != mScrollable) {
1969 mScrollable = scrollable;
1970 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07001971 updateForwardAndBackwardScrollability();
1972 }
1973 }
1974
1975 private void updateForwardAndBackwardScrollability() {
1976 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
1977 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
1978 boolean changed = forwardScrollable != mForwardScrollable
1979 || backwardsScrollable != mBackwardScrollable;
1980 mForwardScrollable = forwardScrollable;
1981 mBackwardScrollable = backwardsScrollable;
1982 if (changed) {
1983 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07001984 }
Selim Cinek67b22602014-03-10 15:40:16 +01001985 }
1986
Selim Cinek6811d722016-01-19 17:53:12 -08001987 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08001988 // No need to update the background color if it's not being drawn.
1989 if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08001990 return;
1991 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08001992
Selim Cinek6811d722016-01-19 17:53:12 -08001993 updateBackgroundBounds();
1994 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07001995 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
1996 || areBoundsAnimating();
1997 if (!isExpanded()) {
1998 abortBackgroundAnimators();
1999 animate = false;
2000 }
2001 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002002 startBackgroundAnimation();
2003 } else {
2004 mCurrentBounds.set(mBackgroundBounds);
2005 applyCurrentBackgroundBounds();
2006 }
2007 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002008 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002009 }
Selim Cinek614576e2016-01-20 10:54:09 -08002010 mAnimateNextBackgroundBottom = false;
2011 mAnimateNextBackgroundTop = false;
2012 }
2013
Selim Cinek54680902016-10-19 16:49:44 -07002014 private void abortBackgroundAnimators() {
2015 if (mBottomAnimator != null) {
2016 mBottomAnimator.cancel();
2017 }
2018 if (mTopAnimator != null) {
2019 mTopAnimator.cancel();
2020 }
2021 }
2022
Selim Cinek614576e2016-01-20 10:54:09 -08002023 private boolean areBoundsAnimating() {
2024 return mBottomAnimator != null || mTopAnimator != null;
2025 }
2026
2027 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002028 // left and right are always instantly applied
2029 mCurrentBounds.left = mBackgroundBounds.left;
2030 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002031 startBottomAnimation();
2032 startTopAnimation();
2033 }
2034
2035 private void startTopAnimation() {
2036 int previousEndValue = mEndAnimationRect.top;
2037 int newEndValue = mBackgroundBounds.top;
2038 ObjectAnimator previousAnimator = mTopAnimator;
2039 if (previousAnimator != null && previousEndValue == newEndValue) {
2040 return;
2041 }
2042 if (!mAnimateNextBackgroundTop) {
2043 // just a local update was performed
2044 if (previousAnimator != null) {
2045 // we need to increase all animation keyframes of the previous animator by the
2046 // relative change to the end value
2047 int previousStartValue = mStartAnimationRect.top;
2048 PropertyValuesHolder[] values = previousAnimator.getValues();
2049 values[0].setIntValues(previousStartValue, newEndValue);
2050 mStartAnimationRect.top = previousStartValue;
2051 mEndAnimationRect.top = newEndValue;
2052 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2053 return;
2054 } else {
2055 // no new animation needed, let's just apply the value
2056 setBackgroundTop(newEndValue);
2057 return;
2058 }
2059 }
2060 if (previousAnimator != null) {
2061 previousAnimator.cancel();
2062 }
2063 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2064 mCurrentBounds.top, newEndValue);
2065 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2066 animator.setInterpolator(interpolator);
2067 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2068 // remove the tag when the animation is finished
2069 animator.addListener(new AnimatorListenerAdapter() {
2070 @Override
2071 public void onAnimationEnd(Animator animation) {
2072 mStartAnimationRect.top = -1;
2073 mEndAnimationRect.top = -1;
2074 mTopAnimator = null;
2075 }
2076 });
2077 animator.start();
2078 mStartAnimationRect.top = mCurrentBounds.top;
2079 mEndAnimationRect.top = newEndValue;
2080 mTopAnimator = animator;
2081 }
2082
2083 private void startBottomAnimation() {
2084 int previousStartValue = mStartAnimationRect.bottom;
2085 int previousEndValue = mEndAnimationRect.bottom;
2086 int newEndValue = mBackgroundBounds.bottom;
2087 ObjectAnimator previousAnimator = mBottomAnimator;
2088 if (previousAnimator != null && previousEndValue == newEndValue) {
2089 return;
2090 }
2091 if (!mAnimateNextBackgroundBottom) {
2092 // just a local update was performed
2093 if (previousAnimator != null) {
2094 // we need to increase all animation keyframes of the previous animator by the
2095 // relative change to the end value
2096 PropertyValuesHolder[] values = previousAnimator.getValues();
2097 values[0].setIntValues(previousStartValue, newEndValue);
2098 mStartAnimationRect.bottom = previousStartValue;
2099 mEndAnimationRect.bottom = newEndValue;
2100 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2101 return;
2102 } else {
2103 // no new animation needed, let's just apply the value
2104 setBackgroundBottom(newEndValue);
2105 return;
2106 }
2107 }
2108 if (previousAnimator != null) {
2109 previousAnimator.cancel();
2110 }
2111 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2112 mCurrentBounds.bottom, newEndValue);
2113 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2114 animator.setInterpolator(interpolator);
2115 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2116 // remove the tag when the animation is finished
2117 animator.addListener(new AnimatorListenerAdapter() {
2118 @Override
2119 public void onAnimationEnd(Animator animation) {
2120 mStartAnimationRect.bottom = -1;
2121 mEndAnimationRect.bottom = -1;
2122 mBottomAnimator = null;
2123 }
2124 });
2125 animator.start();
2126 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2127 mEndAnimationRect.bottom = newEndValue;
2128 mBottomAnimator = animator;
2129 }
2130
2131 private void setBackgroundTop(int top) {
2132 mCurrentBounds.top = top;
2133 applyCurrentBackgroundBounds();
2134 }
2135
2136 public void setBackgroundBottom(int bottom) {
2137 mCurrentBounds.bottom = bottom;
2138 applyCurrentBackgroundBounds();
2139 }
2140
2141 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002142 // If the background of the notification is not being drawn, then there is no need to
2143 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2144 if (!mShouldDrawNotificationBackground) {
2145 return;
2146 }
2147
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002148 mScrimController.setExcludedBackgroundArea(
Selim Cinek48ff9b42016-11-09 19:31:51 -08002149 mFadingOut || mParentNotFullyVisible || mAmbientState.isDark() || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002150 : mCurrentBounds);
Selim Cinek614576e2016-01-20 10:54:09 -08002151 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002152 }
2153
2154 /**
2155 * Update the background bounds to the new desired bounds
2156 */
2157 private void updateBackgroundBounds() {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01002158 if (mAmbientState.isPanelFullWidth()) {
2159 mBackgroundBounds.left = 0;
2160 mBackgroundBounds.right = getWidth();
2161 } else {
2162 getLocationInWindow(mTempInt2);
2163 mBackgroundBounds.left = mTempInt2[0];
2164 mBackgroundBounds.right = mTempInt2[0] + getWidth();
2165 }
Selim Cinek614576e2016-01-20 10:54:09 -08002166 if (!mIsExpanded) {
2167 mBackgroundBounds.top = 0;
2168 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002169 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002170 }
2171 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002172 int top = 0;
2173 if (firstView != null) {
Selim Cinek0cfbef42016-11-09 19:06:36 -08002174 int finalTranslationY = (int) ViewState.getFinalTranslationY(firstView);
Selim Cinek614576e2016-01-20 10:54:09 -08002175 if (mAnimateNextBackgroundTop
2176 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2177 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002178 // we're ending up at the same location as we are now, lets just skip the animation
2179 top = finalTranslationY;
2180 } else {
2181 top = (int) firstView.getTranslationY();
2182 }
2183 }
Selim Cinekeccb5de2016-10-28 15:04:05 -07002184 ActivatableNotificationView lastView = mShelf.hasItemsInStableShelf()
2185 ? mShelf
2186 : mLastVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002187 int bottom = 0;
2188 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002189 int finalTranslationY;
2190 if (lastView == mShelf) {
2191 finalTranslationY = (int) mShelf.getTranslationY();
2192 } else {
2193 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2194 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002195 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002196 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek6811d722016-01-19 17:53:12 -08002197 finalBottom = Math.min(finalBottom, getHeight());
Selim Cinek614576e2016-01-20 10:54:09 -08002198 if (mAnimateNextBackgroundBottom
2199 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2200 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002201 // we're ending up at the same location as we are now, lets just skip the animation
2202 bottom = finalBottom;
2203 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002204 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2205 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002206 bottom = Math.min(bottom, getHeight());
2207 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002208 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002209 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002210 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002211 }
Selim Cinek355652a2016-12-07 13:32:12 -08002212 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002213 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002214 } else {
2215 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002216 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002217 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002218 mBackgroundBounds.top = top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002219 mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
Selim Cinek6811d722016-01-19 17:53:12 -08002220 }
2221
Selim Cinek614576e2016-01-20 10:54:09 -08002222 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2223 int childCount = getChildCount();
2224 for (int i = 0; i < childCount; i++) {
2225 View child = getChildAt(i);
2226 if (child.getVisibility() != View.GONE
2227 && child instanceof ExpandableNotificationRow) {
2228 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2229 if (row.isPinned()) {
2230 return row;
2231 }
2232 }
2233 }
2234 return null;
2235 }
2236
2237 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002238 int childCount = getChildCount();
2239 for (int i = childCount - 1; i >= 0; i--) {
2240 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002241 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2242 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002243 return (ActivatableNotificationView) child;
2244 }
2245 }
2246 return null;
2247 }
2248
Selim Cinek614576e2016-01-20 10:54:09 -08002249 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002250 int childCount = getChildCount();
2251 for (int i = 0; i < childCount; i++) {
2252 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002253 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2254 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002255 return (ActivatableNotificationView) child;
2256 }
2257 }
2258 return null;
2259 }
2260
Selim Cinek67b22602014-03-10 15:40:16 +01002261 /**
2262 * Fling the scroll view
2263 *
2264 * @param velocityY The initial velocity in the Y direction. Positive
2265 * numbers mean that the finger/cursor is moving down the screen,
2266 * which means we want to scroll towards the top.
2267 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002268 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002269 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002270 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002271
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002272 float topAmount = getCurrentOverScrollAmount(true);
2273 float bottomAmount = getCurrentOverScrollAmount(false);
2274 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002275 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002276 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002277 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002278 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002279 * mOverflingDistance + topAmount;
2280 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002281 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002282 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002283 mMaxOverScroll = Math.abs(velocityY) / 1000f
2284 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2285 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002286 } else {
2287 // it will be set once we reach the boundary
2288 mMaxOverScroll = 0.0f;
2289 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002290 int minScrollY = Math.max(0, scrollRange);
2291 if (mExpandedInThisMotion) {
2292 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2293 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002294 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0,
Selim Cinek94ab18c2016-02-25 12:35:51 -08002295 minScrollY, 0, mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002296
2297 postInvalidateOnAnimation();
2298 }
2299 }
2300
Selim Cinek1408eb52014-06-02 14:45:38 +02002301 /**
2302 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2303 * overScroll view (i.e QS).
2304 */
2305 private boolean shouldOverScrollFling(int initialVelocity) {
2306 float topOverScroll = getCurrentOverScrollAmount(true);
2307 return mScrolledToTopOnFirstDown
2308 && !mExpandedInThisMotion
2309 && topOverScroll > mMinTopOverScrollToEscape
2310 && initialVelocity > 0;
2311 }
2312
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002313 /**
2314 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2315 * account.
2316 *
2317 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002318 * @param animate whether to animate the change
2319 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2320 * {@code qsHeight} is the final top padding
2321 */
Jason Monk16ac3772016-02-10 15:39:21 -05002322 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002323 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002324 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002325 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002326 if (topPadding + minStackHeight > getHeight()) {
2327 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002328 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002329 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002330 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002331 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002332 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002333 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002334 }
2335
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002336 public int getLayoutMinHeight() {
Selim Cinek48ff9b42016-11-09 19:31:51 -08002337 return mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002338 }
2339
Selim Cineka981d082016-08-03 14:45:31 -07002340 public int getFirstChildIntrinsicHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002341 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002342 int firstChildMinHeight = firstChild != null
2343 ? firstChild.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002344 : mEmptyShadeView != null
Selim Cineka981d082016-08-03 14:45:31 -07002345 ? mEmptyShadeView.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002346 : mCollapsedSize;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002347 if (mOwnScrollY > 0) {
2348 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2349 }
Selim Cinek94c2d822016-07-13 18:50:04 -07002350 return firstChildMinHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002351 }
2352
2353 public float getTopPaddingOverflow() {
2354 return mTopPaddingOverflow;
2355 }
2356
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002357 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002358 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002359 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002360 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002361 int shelfHeight = 0;
2362 if (mLastVisibleBackgroundChild != null) {
2363 shelfHeight = mShelf.getIntrinsicHeight();
2364 }
2365 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002366 }
2367
Selim Cinek1408eb52014-06-02 14:45:38 +02002368 private int clampPadding(int desiredPadding) {
2369 return Math.max(desiredPadding, mIntrinsicPadding);
2370 }
2371
Selim Cinekfed1ab62014-06-17 14:10:33 -07002372 private float getRubberBandFactor(boolean onTop) {
2373 if (!onTop) {
2374 return RUBBER_BAND_FACTOR_NORMAL;
2375 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002376 if (mExpandedInThisMotion) {
2377 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002378 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002379 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2380 } else if (mScrolledToTopOnFirstDown) {
2381 return 1.0f;
2382 }
2383 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002384 }
2385
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002386 /**
2387 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2388 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2389 * overscroll view (e.g. expand QS).
2390 */
2391 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002392 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002393 || !mScrolledToTopOnFirstDown;
2394 }
2395
Selim Cinek67b22602014-03-10 15:40:16 +01002396 private void endDrag() {
2397 setIsBeingDragged(false);
2398
2399 recycleVelocityTracker();
2400
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002401 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2402 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2403 }
2404 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2405 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2406 }
Selim Cinek67b22602014-03-10 15:40:16 +01002407 }
2408
Jorim Jaggi56306252014-07-03 00:40:09 +02002409 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2410 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2411 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2412 }
2413
Selim Cinek67b22602014-03-10 15:40:16 +01002414 @Override
2415 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002416 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002417 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002418 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002419 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002420 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2421 }
Selim Cinek67b22602014-03-10 15:40:16 +01002422 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002423 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002424 scrollWantsIt = onInterceptTouchEventScroll(ev);
2425 }
2426 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002427 if (!mIsBeingDragged
2428 && !mExpandingNotification
2429 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002430 && !mOnlyScrollingInThisMotion
2431 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002432 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2433 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002434 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2435 }
2436
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002437 private void handleEmptySpaceClick(MotionEvent ev) {
2438 switch (ev.getActionMasked()) {
2439 case MotionEvent.ACTION_MOVE:
2440 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2441 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2442 mTouchIsClick = false;
2443 }
2444 break;
2445 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002446 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002447 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2448 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2449 }
2450 break;
2451 }
2452 }
2453
Selim Cinek1408eb52014-06-02 14:45:38 +02002454 private void initDownStates(MotionEvent ev) {
2455 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2456 mExpandedInThisMotion = false;
2457 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002458 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002459 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002460 mTouchIsClick = true;
2461 mInitialTouchX = ev.getX();
2462 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002463 }
Selim Cinek67b22602014-03-10 15:40:16 +01002464 }
2465
Selim Cinekef5127e2015-12-21 16:55:58 -08002466 public void setChildTransferInProgress(boolean childTransferInProgress) {
2467 mChildTransferInProgress = childTransferInProgress;
2468 }
2469
Christoph Studer068f5922014-04-08 17:43:07 -04002470 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002471 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002472 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002473 // we only call our internal methods if this is actually a removal and not just a
2474 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002475 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002476 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002477 }
2478 }
2479
Selim Cinek197823d2016-03-24 13:06:00 -07002480 @Override
2481 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2482 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2483 if (disallowIntercept) {
2484 mSwipeHelper.removeLongPressCallback();
2485 }
2486 }
2487
Selim Cineka5703182016-05-11 21:23:16 -04002488 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002489 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002490 // This is only a position change, don't do anything special
2491 return;
2492 }
Selim Cinekd1395642016-04-28 12:22:42 -07002493 ExpandableView expandableView = (ExpandableView) child;
2494 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002495 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002496 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002497 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002498 if (animationGenerated) {
2499 if (!mSwipedOutViews.contains(child)) {
Selim Cineka5703182016-05-11 21:23:16 -04002500 container.getOverlay().add(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002501 } else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002502 container.addTransientView(child, 0);
2503 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002504 }
2505 } else {
2506 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002507 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002508 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002509
2510 // Make sure the clipRect we might have set is removed
Mady Mellorc128f222016-04-26 11:42:46 -07002511 expandableView.setClipTopAmount(0);
Selim Cineke9bad242016-06-15 11:46:37 -07002512
2513 focusNextViewIfFocused(child);
2514 }
2515
2516 private void focusNextViewIfFocused(View view) {
2517 if (view instanceof ExpandableNotificationRow) {
2518 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2519 if (row.shouldRefocusOnDismiss()) {
2520 View nextView = row.getChildAfterViewWhenDismissed();
2521 if (nextView == null) {
2522 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2523 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2524 ? groupParentWhenDismissed.getTranslationY()
2525 : view.getTranslationY());
2526 }
2527 if (nextView != null) {
2528 nextView.requestAccessibilityFocus();
2529 }
2530 }
2531 }
2532
Selim Cinekc27437b2014-05-14 10:23:33 +02002533 }
2534
Selim Cinekb5605e52015-02-20 18:21:41 +01002535 private boolean isChildInGroup(View child) {
2536 return child instanceof ExpandableNotificationRow
2537 && mGroupManager.isChildInGroupWithSummary(
2538 ((ExpandableNotificationRow) child).getStatusBarNotification());
2539 }
2540
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002541 /**
2542 * Generate a remove animation for a child view.
2543 *
2544 * @param child The view to generate the remove animation for.
2545 * @return Whether an animation was generated.
2546 */
2547 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002548 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002549 mAddedHeadsUpChildren.remove(child);
2550 return false;
2551 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002552 if (isClickedHeadsUp(child)) {
2553 // An animation is already running, add it to the Overlay
2554 mClearOverlayViewsWhenFinished.add(child);
2555 return true;
2556 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002557 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002558 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002559 // Generate Animations
2560 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002561 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002562 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002563 } else {
2564 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002565 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002566 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002567 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002568 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002569 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002570 }
2571
Selim Cinek0fccc722015-07-29 17:04:36 -07002572 private boolean isClickedHeadsUp(View child) {
2573 return HeadsUpManager.isClickedHeadsUpNotification(child);
2574 }
2575
Selim Cineke0890e52015-06-17 11:17:08 -07002576 /**
2577 * Remove a removed child view from the heads up animations if it was just added there
2578 *
2579 * @return whether any child was removed from the list to animate
2580 */
2581 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2582 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002583 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2584 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002585 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002586 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002587 mTmpList.add(eventPair);
2588 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002589 }
2590 }
Selim Cineke0890e52015-06-17 11:17:08 -07002591 if (hasAddEvent) {
2592 // This child was just added lets remove all events.
2593 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002594 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002595 }
2596 mTmpList.clear();
2597 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002598 }
2599
Selim Cinek572bbd42014-04-25 16:43:27 +02002600 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002601 * @param child the child to query
2602 * @return whether a view is not a top level child but a child notification and that group is
2603 * not expanded
2604 */
2605 private boolean isChildInInvisibleGroup(View child) {
2606 if (child instanceof ExpandableNotificationRow) {
2607 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2608 ExpandableNotificationRow groupSummary =
2609 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2610 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002611 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002612 }
2613 }
2614 return false;
2615 }
2616
2617 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002618 * Updates the scroll position when a child was removed
2619 *
2620 * @param removedChild the removed child
2621 */
Selim Cinek61633a82016-01-25 15:54:10 -08002622 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002623 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002624 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2625 int padding;
2626 if (increasedPaddingAmount >= 0) {
2627 padding = (int) NotificationUtils.interpolate(
2628 mPaddingBetweenElements,
2629 mIncreasedPaddingBetweenElements,
2630 increasedPaddingAmount);
2631 } else {
2632 padding = (int) NotificationUtils.interpolate(
2633 0,
2634 mPaddingBetweenElements,
2635 1.0f + increasedPaddingAmount);
2636 }
Selim Cinek61633a82016-01-25 15:54:10 -08002637 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002638 int endPosition = startingPosition + childHeight;
2639 if (endPosition <= mOwnScrollY) {
2640 // This child is fully scrolled of the top, so we have to deduct its height from the
2641 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002642 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002643 } else if (startingPosition < mOwnScrollY) {
2644 // This child is currently being scrolled into, set the scroll position to the start of
2645 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002646 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002647 }
2648 }
2649
Selim Cinekd7c4e002014-07-04 18:36:42 +02002650 private int getIntrinsicHeight(View view) {
2651 if (view instanceof ExpandableView) {
2652 ExpandableView expandableView = (ExpandableView) view;
2653 return expandableView.getIntrinsicHeight();
2654 }
2655 return view.getHeight();
2656 }
2657
Adrian Roos4a579672016-05-24 16:54:37 -07002658 private int getPositionInLinearLayout(View requestedView) {
2659 ExpandableNotificationRow childInGroup = null;
2660 ExpandableNotificationRow requestedRow = null;
2661 if (isChildInGroup(requestedView)) {
2662 // We're asking for a child in a group. Calculate the position of the parent first,
2663 // then within the parent.
2664 childInGroup = (ExpandableNotificationRow) requestedView;
2665 requestedView = requestedRow = childInGroup.getNotificationParent();
2666 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002667 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002668 float previousPaddingRequest = mPaddingBetweenElements;
2669 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002670 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002671 ExpandableView child = (ExpandableView) getChildAt(i);
2672 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002673 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002674 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002675 float padding;
2676 if (increasedPaddingAmount >= 0.0f) {
2677 padding = (int) NotificationUtils.interpolate(
2678 previousPaddingRequest,
2679 mIncreasedPaddingBetweenElements,
2680 increasedPaddingAmount);
2681 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002682 mPaddingBetweenElements,
2683 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002684 increasedPaddingAmount);
2685 } else {
2686 int ownPadding = (int) NotificationUtils.interpolate(
2687 0,
2688 mPaddingBetweenElements,
2689 1.0f + increasedPaddingAmount);
2690 if (previousPaddingAmount > 0.0f) {
2691 padding = (int) NotificationUtils.interpolate(
2692 ownPadding,
2693 mIncreasedPaddingBetweenElements,
2694 previousPaddingAmount);
2695 } else {
2696 padding = ownPadding;
2697 }
2698 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08002699 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002700 if (position != 0) {
2701 position += padding;
2702 }
2703 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002704 }
Adrian Roos4a579672016-05-24 16:54:37 -07002705 if (child == requestedView) {
2706 if (requestedRow != null) {
2707 position += requestedRow.getPositionOfChild(childInGroup);
2708 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002709 return position;
2710 }
Selim Cinek61633a82016-01-25 15:54:10 -08002711 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02002712 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002713 }
2714 }
2715 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02002716 }
2717
2718 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002719 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02002720 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002721 onViewAddedInternal(child);
2722 }
2723
Selim Cinek614576e2016-01-20 10:54:09 -08002724 private void updateFirstAndLastBackgroundViews() {
2725 ActivatableNotificationView firstChild = getFirstChildWithBackground();
2726 ActivatableNotificationView lastChild = getLastChildWithBackground();
2727 if (mAnimationsEnabled && mIsExpanded) {
2728 mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
2729 mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
2730 } else {
2731 mAnimateNextBackgroundTop = false;
2732 mAnimateNextBackgroundBottom = false;
2733 }
2734 mFirstVisibleBackgroundChild = firstChild;
2735 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08002736 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek614576e2016-01-20 10:54:09 -08002737 }
2738
Selim Cinekb5605e52015-02-20 18:21:41 +01002739 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002740 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02002741 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00002742 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002743 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02002744 updateChronometerForChild(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002745 }
2746
Selim Cinekd06c41c2015-07-06 14:51:36 -07002747 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002748 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002749 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002750 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07002751 }
2752 }
2753
Selim Cinekd1395642016-04-28 12:22:42 -07002754 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
2755 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01002756 }
2757
2758 public void notifyGroupChildAdded(View row) {
2759 onViewAddedInternal(row);
2760 }
2761
Jorim Jaggi75c95042014-05-16 19:09:59 +02002762 public void setAnimationsEnabled(boolean animationsEnabled) {
2763 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02002764 updateNotificationAnimationStates();
2765 }
2766
2767 private void updateNotificationAnimationStates() {
Selim Cinekcd5b22f2016-03-08 16:15:41 -08002768 boolean running = mAnimationsEnabled || mPulsing;
Selim Cinek09bd29d2017-02-03 15:30:28 -08002769 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02002770 int childCount = getChildCount();
2771 for (int i = 0; i < childCount; i++) {
2772 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07002773 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02002774 updateAnimationState(running, child);
2775 }
2776 }
2777
Selim Cinek51ae05d2014-09-09 15:51:38 +02002778 private void updateAnimationState(View child) {
Selim Cinekcd5b22f2016-03-08 16:15:41 -08002779 updateAnimationState((mAnimationsEnabled || mPulsing)
2780 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002781 }
2782
2783
Selim Cinekcab4a602014-09-03 14:47:57 +02002784 private void updateAnimationState(boolean running, View child) {
2785 if (child instanceof ExpandableNotificationRow) {
2786 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2787 row.setIconAnimationRunning(running);
2788 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02002789 }
2790
2791 public boolean isAddOrRemoveAnimationPending() {
2792 return mNeedsAnimation
2793 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
2794 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002795 /**
2796 * Generate an animation for an added child view.
2797 *
2798 * @param child The view to be added.
Jorim Jaggif6411742014-08-05 17:10:43 +00002799 * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002800 */
Jorim Jaggif6411742014-08-05 17:10:43 +00002801 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002802 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002803 // Generate Animations
2804 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00002805 if (fromMoreCard) {
2806 mFromMoreCardAdditions.add(child);
2807 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002808 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02002809 }
Adrian Roos777ef562015-12-01 17:37:14 -08002810 if (isHeadsUp(child) && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07002811 mAddedHeadsUpChildren.add(child);
2812 mChildrenToAddAnimated.remove(child);
2813 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002814 }
2815
2816 /**
2817 * Change the position of child to a new location
2818 *
2819 * @param child the view to change the position for
2820 * @param newIndex the new index
2821 */
2822 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04002823 int currentIndex = indexOfChild(child);
2824 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002825 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08002826 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02002827 removeView(child);
2828 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08002829 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02002830 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04002831 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002832 mChildrenChangingPositions.add(child);
2833 mNeedsAnimation = true;
2834 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002835 }
2836 }
2837
Selim Cinekf4c19962014-05-01 21:55:31 +02002838 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002839 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002840 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002841 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002842 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002843 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07002844 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02002845 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
2846 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002847 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08002848 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08002849 updateViewShadows();
Selim Cinekf4c19962014-05-01 21:55:31 +02002850 } else {
2851 applyCurrentState();
2852 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02002853 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02002854 }
2855
2856 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07002857 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02002858 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002859 generateChildAdditionEvents();
2860 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02002861 generateSnapBackEvents();
2862 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002863 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02002864 generateActivateEvent();
2865 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02002866 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04002867 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02002868 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02002869 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01002870 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02002871 generateAnimateEverythingEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002872 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002873 }
2874
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002875 private void generateHeadsUpAnimationEvents() {
2876 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07002877 ExpandableNotificationRow row = eventPair.first;
2878 boolean isHeadsUp = eventPair.second;
2879 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
2880 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07002881 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07002882 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07002883 type = row.wasJustClicked()
2884 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
2885 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07002886 if (row.isChildInGroup()) {
2887 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07002888 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07002889 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04002890 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08002891 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04002892 if (viewState == null) {
2893 // A view state was never generated for this view, so we don't need to animate
2894 // this. This may happen with notification children.
2895 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07002896 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04002897 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
2898 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
2899 // Our custom add animation
2900 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
2901 } else {
2902 // Normal add animation
2903 type = AnimationEvent.ANIMATION_TYPE_ADD;
2904 }
2905 onBottom = !pinnedAndClosed;
2906 }
Selim Cineka59ecc32015-04-07 10:51:49 -07002907 }
2908 AnimationEvent event = new AnimationEvent(row, type);
2909 event.headsUpFromBottom = onBottom;
2910 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002911 }
2912 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07002913 mAddedHeadsUpChildren.clear();
2914 }
2915
Selim Cinekbbcebde2016-11-09 18:28:20 -08002916 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07002917 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
2918 return false;
2919 }
2920 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002921 }
2922
Selim Cinekb5605e52015-02-20 18:21:41 +01002923 private void generateGroupExpansionEvent() {
2924 // Generate a group expansion/collapsing event if there is such a group at all
2925 if (mExpandedGroupView != null) {
2926 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
2927 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
2928 mExpandedGroupView = null;
2929 }
2930 }
2931
Selim Cineka5e211b2014-08-11 17:35:48 +02002932 private void generateViewResizeEvent() {
2933 if (mNeedViewResizeAnimation) {
2934 mAnimationEvents.add(
2935 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
2936 }
2937 mNeedViewResizeAnimation = false;
2938 }
2939
Selim Cinekeb973562014-05-02 17:07:49 +02002940 private void generateSnapBackEvents() {
2941 for (View child : mSnappedBackChildren) {
2942 mAnimationEvents.add(new AnimationEvent(child,
2943 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
2944 }
2945 mSnappedBackChildren.clear();
2946 }
2947
2948 private void generateDragEvents() {
2949 for (View child : mDragAnimPendingChildren) {
2950 mAnimationEvents.add(new AnimationEvent(child,
2951 AnimationEvent.ANIMATION_TYPE_START_DRAG));
2952 }
2953 mDragAnimPendingChildren.clear();
2954 }
2955
Selim Cinek572bbd42014-04-25 16:43:27 +02002956 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02002957 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002958 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
2959 int animationType = childWasSwipedOut
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002960 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
2961 : AnimationEvent.ANIMATION_TYPE_REMOVE;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002962 AnimationEvent event = new AnimationEvent(child, animationType);
2963
2964 // we need to know the view after this one
2965 event.viewAfterChangingView = getFirstChildBelowTranlsationY(child.getTranslationY());
2966 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07002967 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002968 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002969 mChildrenToRemoveAnimated.clear();
2970 }
2971
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002972 private void generatePositionChangeEvents() {
2973 for (View child : mChildrenChangingPositions) {
2974 mAnimationEvents.add(new AnimationEvent(child,
2975 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
2976 }
2977 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01002978 if (mGenerateChildOrderChangedEvent) {
2979 mAnimationEvents.add(new AnimationEvent(null,
2980 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
2981 mGenerateChildOrderChangedEvent = false;
2982 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002983 }
2984
Selim Cinek572bbd42014-04-25 16:43:27 +02002985 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02002986 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002987 if (mFromMoreCardAdditions.contains(child)) {
2988 mAnimationEvents.add(new AnimationEvent(child,
2989 AnimationEvent.ANIMATION_TYPE_ADD,
2990 StackStateAnimator.ANIMATION_DURATION_STANDARD));
2991 } else {
2992 mAnimationEvents.add(new AnimationEvent(child,
2993 AnimationEvent.ANIMATION_TYPE_ADD));
2994 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002995 }
2996 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002997 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04002998 }
2999
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003000 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003001 if (mTopPaddingNeedsAnimation) {
3002 mAnimationEvents.add(
3003 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
3004 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003005 mTopPaddingNeedsAnimation = false;
3006 }
3007
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003008 private void generateActivateEvent() {
3009 if (mActivateNeedsAnimation) {
3010 mAnimationEvents.add(
3011 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3012 }
3013 mActivateNeedsAnimation = false;
3014 }
3015
Selim Cinekd9acca52014-09-01 22:33:25 +02003016 private void generateAnimateEverythingEvent() {
3017 if (mEverythingNeedsAnimation) {
3018 mAnimationEvents.add(
3019 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3020 }
3021 mEverythingNeedsAnimation = false;
3022 }
3023
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003024 private void generateDimmedEvent() {
3025 if (mDimmedNeedsAnimation) {
3026 mAnimationEvents.add(
3027 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3028 }
3029 mDimmedNeedsAnimation = false;
3030 }
3031
Jorim Jaggiae441282014-08-01 02:45:18 +02003032 private void generateHideSensitiveEvent() {
3033 if (mHideSensitiveNeedsAnimation) {
3034 mAnimationEvents.add(
3035 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3036 }
3037 mHideSensitiveNeedsAnimation = false;
3038 }
3039
John Spurlockbf370992014-06-17 13:58:31 -04003040 private void generateDarkEvent() {
3041 if (mDarkNeedsAnimation) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003042 AnimationEvent ev = new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DARK);
3043 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3044 mAnimationEvents.add(ev);
Selim Cinek972123d2016-05-03 14:25:58 -07003045 startBackgroundFadeIn();
John Spurlockbf370992014-06-17 13:58:31 -04003046 }
3047 mDarkNeedsAnimation = false;
3048 }
3049
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003050 private void generateGoToFullShadeEvent() {
3051 if (mGoToFullShadeNeedsAnimation) {
3052 mAnimationEvents.add(
3053 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3054 }
3055 mGoToFullShadeNeedsAnimation = false;
3056 }
3057
Selim Cinek67b22602014-03-10 15:40:16 +01003058 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003059 if (!isScrollingEnabled()) {
3060 return false;
3061 }
Selim Cinek67b22602014-03-10 15:40:16 +01003062 /*
3063 * This method JUST determines whether we want to intercept the motion.
3064 * If we return true, onMotionEvent will be called and we do the actual
3065 * scrolling there.
3066 */
3067
3068 /*
3069 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003070 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003071 * motion.
3072 */
3073 final int action = ev.getAction();
3074 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3075 return true;
3076 }
3077
Selim Cinek67b22602014-03-10 15:40:16 +01003078 switch (action & MotionEvent.ACTION_MASK) {
3079 case MotionEvent.ACTION_MOVE: {
3080 /*
3081 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003082 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003083 */
3084
3085 /*
3086 * Locally do absolute value. mLastMotionY is set to the y value
3087 * of the down event.
3088 */
3089 final int activePointerId = mActivePointerId;
3090 if (activePointerId == INVALID_POINTER) {
3091 // If we don't have a valid id, the touch down wasn't on content.
3092 break;
3093 }
3094
3095 final int pointerIndex = ev.findPointerIndex(activePointerId);
3096 if (pointerIndex == -1) {
3097 Log.e(TAG, "Invalid pointerId=" + activePointerId
3098 + " in onInterceptTouchEvent");
3099 break;
3100 }
3101
3102 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003103 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003104 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003105 final int xDiff = Math.abs(x - mDownX);
3106 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003107 setIsBeingDragged(true);
3108 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003109 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003110 initVelocityTrackerIfNotExists();
3111 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003112 }
3113 break;
3114 }
3115
3116 case MotionEvent.ACTION_DOWN: {
3117 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303118 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek67b22602014-03-10 15:40:16 +01003119 if (getChildAtPosition(ev.getX(), y) == null) {
3120 setIsBeingDragged(false);
3121 recycleVelocityTracker();
3122 break;
3123 }
3124
3125 /*
3126 * Remember location of down touch.
3127 * ACTION_DOWN always refers to pointer index 0.
3128 */
3129 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003130 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003131 mActivePointerId = ev.getPointerId(0);
3132
3133 initOrResetVelocityTracker();
3134 mVelocityTracker.addMovement(ev);
3135 /*
3136 * If being flinged and user touches the screen, initiate drag;
3137 * otherwise don't. mScroller.isFinished should be false when
3138 * being flinged.
3139 */
3140 boolean isBeingDragged = !mScroller.isFinished();
3141 setIsBeingDragged(isBeingDragged);
3142 break;
3143 }
3144
3145 case MotionEvent.ACTION_CANCEL:
3146 case MotionEvent.ACTION_UP:
3147 /* Release the drag */
3148 setIsBeingDragged(false);
3149 mActivePointerId = INVALID_POINTER;
3150 recycleVelocityTracker();
3151 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
3152 postInvalidateOnAnimation();
3153 }
3154 break;
3155 case MotionEvent.ACTION_POINTER_UP:
3156 onSecondaryPointerUp(ev);
3157 break;
3158 }
3159
3160 /*
3161 * The only time we want to intercept motion events is if we are in the
3162 * drag mode.
3163 */
3164 return mIsBeingDragged;
3165 }
3166
Muyuan Li333a4fc2016-04-16 17:13:46 -07003167 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3168 return new StackScrollAlgorithm(context);
3169 }
3170
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003171 /**
3172 * @return Whether the specified motion event is actually happening over the content.
3173 */
3174 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003175 return isInContentBounds(event.getY());
3176 }
3177
3178 /**
3179 * @return Whether a y coordinate is inside the content.
3180 */
3181 public boolean isInContentBounds(float y) {
3182 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003183 }
3184
Selim Cinek67b22602014-03-10 15:40:16 +01003185 private void setIsBeingDragged(boolean isDragged) {
3186 mIsBeingDragged = isDragged;
3187 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003188 requestDisallowInterceptTouchEvent(true);
Selim Cinek1408eb52014-06-02 14:45:38 +02003189 removeLongPressCallback();
Selim Cinek67b22602014-03-10 15:40:16 +01003190 }
3191 }
3192
3193 @Override
3194 public void onWindowFocusChanged(boolean hasWindowFocus) {
3195 super.onWindowFocusChanged(hasWindowFocus);
3196 if (!hasWindowFocus) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003197 removeLongPressCallback();
Selim Cinek67b22602014-03-10 15:40:16 +01003198 }
3199 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003200
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003201 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003202 public void clearChildFocus(View child) {
3203 super.clearChildFocus(child);
3204 if (mForcedScroll == child) {
3205 mForcedScroll = null;
3206 }
3207 }
3208
3209 @Override
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003210 public void requestDisallowLongPress() {
3211 removeLongPressCallback();
3212 }
3213
Adrian Roosfa139752016-04-27 09:59:08 -07003214 @Override
3215 public void requestDisallowDismiss() {
3216 mDisallowDismissInThisMotion = true;
3217 }
3218
Selim Cinek1408eb52014-06-02 14:45:38 +02003219 public void removeLongPressCallback() {
3220 mSwipeHelper.removeLongPressCallback();
3221 }
3222
Selim Cinekfab078b2014-03-27 22:45:58 +01003223 @Override
3224 public boolean isScrolledToTop() {
3225 return mOwnScrollY == 0;
3226 }
3227
3228 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003229 public boolean isScrolledToBottom() {
3230 return mOwnScrollY >= getScrollRange();
3231 }
3232
3233 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003234 public View getHostView() {
3235 return this;
3236 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003237
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003238 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003239 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003240 }
3241
Selim Cinek1685e632014-04-08 02:27:49 +02003242 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003243 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003244 mAmbientState.setExpansionChanging(true);
Selim Cinek1685e632014-04-08 02:27:49 +02003245 }
3246
3247 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003248 mIsExpansionChanging = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003249 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003250 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003251 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003252 mStatusBar.resetUserExpandedStates();
Selim Cinekf336f4c2014-11-12 16:58:16 +01003253
Selim Cinekd1395642016-04-28 12:22:42 -07003254 // lets make sure nothing is in the overlay / transient anymore
Selim Cineka5703182016-05-11 21:23:16 -04003255 clearTemporaryViews(this);
Selim Cinekd1395642016-04-28 12:22:42 -07003256 for (int i = 0; i < getChildCount(); i++) {
3257 ExpandableView child = (ExpandableView) getChildAt(i);
3258 if (child instanceof ExpandableNotificationRow) {
3259 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Selim Cineka5703182016-05-11 21:23:16 -04003260 clearTemporaryViews(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003261 }
3262 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003263 }
Selim Cinek1685e632014-04-08 02:27:49 +02003264 }
3265
Selim Cineka5703182016-05-11 21:23:16 -04003266 private void clearTemporaryViews(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003267 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003268 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003269 }
Selim Cineka5703182016-05-11 21:23:16 -04003270 if (viewGroup != null) {
3271 viewGroup.getOverlay().clear();
3272 }
Selim Cinekd1395642016-04-28 12:22:42 -07003273 }
3274
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003275 public void onPanelTrackingStarted() {
3276 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003277 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003278 }
3279 public void onPanelTrackingStopped() {
3280 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003281 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003282 }
3283
Selim Cinekb24e0a92015-06-09 20:17:30 -07003284 public void resetScrollPosition() {
3285 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003286 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003287 }
3288
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003289 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003290 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003291 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003292 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003293 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003294 if (!mIsExpanded) {
3295 mGroupManager.collapseAllGroups();
3296 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003297 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003298 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003299 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003300 }
3301 }
3302
3303 private void updateChronometers() {
3304 int childCount = getChildCount();
3305 for (int i = 0; i < childCount; i++) {
3306 updateChronometerForChild(getChildAt(i));
3307 }
3308 }
3309
3310 private void updateChronometerForChild(View child) {
3311 if (child instanceof ExpandableNotificationRow) {
3312 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3313 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003314 }
Selim Cinek1685e632014-04-08 02:27:49 +02003315 }
3316
Jorim Jaggibe565df2014-04-28 17:51:23 +02003317 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003318 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003319 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003320 updateScrollPositionOnExpandInBottom(view);
3321 clampScrollPosition();
Selim Cinekaef92ef2014-06-06 18:06:04 +02003322 notifyHeightChangeListener(view);
Selim Cinekbc243a92016-09-27 16:35:13 -07003323 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3324 ? (ExpandableNotificationRow) view
3325 : null;
3326 if (row != null && (row == mFirstVisibleBackgroundChild
3327 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3328 updateAlgorithmLayoutMinHeight();
3329 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003330 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003331 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003332 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003333 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003334 }
3335
Selim Cineka5e211b2014-08-11 17:35:48 +02003336 @Override
3337 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003338 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003339 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003340 }
3341
Selim Cinekf7a14c02014-07-07 14:01:46 +02003342 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
3343 if (view instanceof ExpandableNotificationRow) {
3344 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003345 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003346 if (row.isSummaryWithChildren()) {
3347 return;
3348 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003349 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003350 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003351 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003352 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003353 }
Selim Cinekdb167372016-11-17 15:41:17 -08003354 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
3355 if (row != mLastVisibleBackgroundChild) {
3356 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3357 }
3358 if (endPosition > layoutEnd) {
3359 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003360 mDisallowScrollingInThisMotion = true;
3361 }
3362 }
3363 }
3364 }
3365
Jorim Jaggibe565df2014-04-28 17:51:23 +02003366 public void setOnHeightChangedListener(
3367 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3368 this.mOnHeightChangedListener = mOnHeightChangedListener;
3369 }
3370
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003371 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3372 mOnEmptySpaceClickListener = listener;
3373 }
3374
Selim Cinek572bbd42014-04-25 16:43:27 +02003375 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003376 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003377 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003378 runAnimationFinishedRunnables();
Selim Cinek0fccc722015-07-29 17:04:36 -07003379 clearViewOverlays();
Selim Cinek8fc78752016-07-13 14:34:56 -07003380 clearHeadsUpDisappearRunning();
3381 }
3382
3383 private void clearHeadsUpDisappearRunning() {
3384 for (int i = 0; i < getChildCount(); i++) {
3385 View view = getChildAt(i);
3386 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003387 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003388 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003389 if (row.isSummaryWithChildren()) {
3390 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003391 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003392 }
3393 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003394 }
3395 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003396 }
3397
3398 private void clearViewOverlays() {
3399 for (View view : mClearOverlayViewsWhenFinished) {
Selim Cineka5703182016-05-11 21:23:16 -04003400 StackStateAnimator.removeFromOverlay(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003401 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003402 }
3403
3404 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003405 for (Runnable runnable : mAnimationFinishedRunnables) {
3406 runnable.run();
3407 }
3408 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003409 }
3410
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003411 /**
3412 * See {@link AmbientState#setDimmed}.
3413 */
3414 public void setDimmed(boolean dimmed, boolean animate) {
3415 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003416 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003417 mDimmedNeedsAnimation = true;
3418 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003419 animateDimmed(dimmed);
3420 } else {
3421 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003422 }
3423 requestChildrenUpdate();
3424 }
3425
Selim Cinekd35c2792016-01-21 13:20:57 -08003426 private void setDimAmount(float dimAmount) {
3427 mDimAmount = dimAmount;
3428 updateBackgroundDimming();
3429 }
3430
3431 private void animateDimmed(boolean dimmed) {
3432 if (mDimAnimator != null) {
3433 mDimAnimator.cancel();
3434 }
3435 float target = dimmed ? 1.0f : 0.0f;
3436 if (target == mDimAmount) {
3437 return;
3438 }
3439 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3440 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3441 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3442 mDimAnimator.addListener(mDimEndListener);
3443 mDimAnimator.addUpdateListener(mDimUpdateListener);
3444 mDimAnimator.start();
3445 }
3446
Jorim Jaggiae441282014-08-01 02:45:18 +02003447 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3448 if (hideSensitive != mAmbientState.isHideSensitive()) {
3449 int childCount = getChildCount();
3450 for (int i = 0; i < childCount; i++) {
3451 ExpandableView v = (ExpandableView) getChildAt(i);
3452 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3453 }
3454 mAmbientState.setHideSensitive(hideSensitive);
3455 if (animate && mAnimationsEnabled) {
3456 mHideSensitiveNeedsAnimation = true;
3457 mNeedsAnimation = true;
3458 }
3459 requestChildrenUpdate();
3460 }
3461 }
3462
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003463 /**
3464 * See {@link AmbientState#setActivatedChild}.
3465 */
Selim Cineka32ab602014-06-11 15:06:01 +02003466 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003467 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003468 if (mAnimationsEnabled) {
3469 mActivateNeedsAnimation = true;
3470 mNeedsAnimation = true;
3471 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003472 requestChildrenUpdate();
3473 }
3474
Selim Cineka32ab602014-06-11 15:06:01 +02003475 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003476 return mAmbientState.getActivatedChild();
3477 }
3478
Selim Cinek572bbd42014-04-25 16:43:27 +02003479 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003480 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003481 if (mListener != null) {
3482 mListener.onChildLocationsChanged(this);
3483 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003484 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003485 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003486 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003487 updateViewShadows();
3488 }
3489
3490 private void updateViewShadows() {
3491 // we need to work around an issue where the shadow would not cast between siblings when
3492 // their z difference is between 0 and 0.1
3493
3494 // Lefts first sort by Z difference
3495 for (int i = 0; i < getChildCount(); i++) {
3496 ExpandableView child = (ExpandableView) getChildAt(i);
3497 if (child.getVisibility() != GONE) {
3498 mTmpSortedChildren.add(child);
3499 }
3500 }
3501 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3502
3503 // Now lets update the shadow for the views
3504 ExpandableView previous = null;
3505 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3506 ExpandableView expandableView = mTmpSortedChildren.get(i);
3507 float translationZ = expandableView.getTranslationZ();
3508 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3509 float diff = otherZ - translationZ;
3510 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3511 // There is no fake shadow to be drawn
3512 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3513 } else {
3514 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003515 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3516 expandableView.setFakeShadowIntensity(
3517 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003518 previous.getOutlineAlpha(), (int) yLocation,
3519 previous.getOutlineTranslation());
3520 }
3521 previous = expandableView;
3522 }
3523
3524 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003525 }
3526
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003527 public void goToFullShade(long delay) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003528 mDismissView.setInvisible();
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003529 mEmptyShadeView.setInvisible();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003530 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003531 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003532 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003533 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003534 }
3535
Selim Cinek1408eb52014-06-02 14:45:38 +02003536 public void cancelExpandHelper() {
3537 mExpandHelper.cancel();
3538 }
3539
3540 public void setIntrinsicPadding(int intrinsicPadding) {
3541 mIntrinsicPadding = intrinsicPadding;
3542 }
3543
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003544 public int getIntrinsicPadding() {
3545 return mIntrinsicPadding;
3546 }
3547
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003548 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003549 * @return the y position of the first notification
3550 */
3551 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003552 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003553 }
3554
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003555 @Override
3556 public boolean shouldDelayChildPressedState() {
3557 return true;
3558 }
3559
Jorim Jaggi457cc352014-06-02 22:47:42 +02003560 /**
John Spurlockbf370992014-06-17 13:58:31 -04003561 * See {@link AmbientState#setDark}.
3562 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003563 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
John Spurlockbf370992014-06-17 13:58:31 -04003564 mAmbientState.setDark(dark);
3565 if (animate && mAnimationsEnabled) {
3566 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003567 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
John Spurlockbf370992014-06-17 13:58:31 -04003568 mNeedsAnimation = true;
Selim Cinek972123d2016-05-03 14:25:58 -07003569 setBackgroundFadeAmount(0.0f);
3570 } else if (!dark) {
3571 setBackgroundFadeAmount(1.0f);
John Spurlockbf370992014-06-17 13:58:31 -04003572 }
3573 requestChildrenUpdate();
Selim Cinek6811d722016-01-19 17:53:12 -08003574 if (dark) {
Selim Cinek6811d722016-01-19 17:53:12 -08003575 mScrimController.setExcludedBackgroundArea(null);
3576 } else {
3577 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08003578 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003579
3580 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003581 updateContentHeight();
3582 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04003583 }
3584
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003585 /**
3586 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
3587 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
3588 * {@link #mAmbientState}'s dark mode is toggled.
3589 */
3590 private void updateWillNotDraw() {
3591 if (mAmbientState.isDark()) {
3592 setWillNotDraw(!DEBUG);
3593 } else {
3594 setWillNotDraw(!mShouldDrawNotificationBackground && !DEBUG);
3595 }
3596 }
3597
Selim Cinek972123d2016-05-03 14:25:58 -07003598 private void setBackgroundFadeAmount(float fadeAmount) {
3599 mBackgroundFadeAmount = fadeAmount;
3600 updateBackgroundDimming();
3601 }
3602
3603 public float getBackgroundFadeAmount() {
3604 return mBackgroundFadeAmount;
3605 }
3606
3607 private void startBackgroundFadeIn() {
3608 ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, BACKGROUND_FADE, 0f, 1f);
3609 int maxLength;
3610 if (mDarkAnimationOriginIndex == AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE
3611 || mDarkAnimationOriginIndex == AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW) {
3612 maxLength = getNotGoneChildCount() - 1;
3613 } else {
3614 maxLength = Math.max(mDarkAnimationOriginIndex,
3615 getNotGoneChildCount() - mDarkAnimationOriginIndex - 1);
3616 }
Selim Cinekea66ca02016-05-24 13:33:47 -07003617 maxLength = Math.max(0, maxLength);
Selim Cinek972123d2016-05-03 14:25:58 -07003618 long delay = maxLength * StackStateAnimator.ANIMATION_DELAY_PER_ELEMENT_DARK;
3619 fadeAnimator.setStartDelay(delay);
3620 fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
3621 fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
3622 fadeAnimator.start();
3623 }
3624
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003625 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07003626 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003627 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3628 }
3629 if (screenLocation.y > getBottomMostNotificationBottom()) {
3630 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
3631 }
3632 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
3633 if (child != null) {
3634 return getNotGoneIndex(child);
3635 } else {
3636 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3637 }
3638 }
3639
3640 private int getNotGoneIndex(View child) {
3641 int count = getChildCount();
3642 int notGoneIndex = 0;
3643 for (int i = 0; i < count; i++) {
3644 View v = getChildAt(i);
3645 if (child == v) {
3646 return notGoneIndex;
3647 }
3648 if (v.getVisibility() != View.GONE) {
3649 notGoneIndex++;
3650 }
3651 }
3652 return -1;
3653 }
3654
Dan Sandlereceda3d2014-07-21 15:35:01 -04003655 public void setDismissView(DismissView dismissView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003656 int index = -1;
3657 if (mDismissView != null) {
3658 index = indexOfChild(mDismissView);
3659 removeView(mDismissView);
3660 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003661 mDismissView = dismissView;
Selim Cinek01af3342016-02-09 19:25:31 -08003662 addView(mDismissView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003663 }
3664
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003665 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003666 int index = -1;
3667 if (mEmptyShadeView != null) {
3668 index = indexOfChild(mEmptyShadeView);
3669 removeView(mEmptyShadeView);
3670 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003671 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08003672 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003673 }
3674
3675 public void updateEmptyShadeView(boolean visible) {
3676 int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
3677 int newVisibility = visible ? VISIBLE : GONE;
3678 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003679 if (newVisibility != GONE) {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003680 if (mEmptyShadeView.willBeGone()) {
3681 mEmptyShadeView.cancelAnimation();
3682 } else {
3683 mEmptyShadeView.setInvisible();
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003684 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003685 mEmptyShadeView.setVisibility(newVisibility);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003686 mEmptyShadeView.setWillBeGone(false);
3687 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003688 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003689 } else {
Selim Cinek20867102014-12-10 17:09:17 +01003690 Runnable onFinishedRunnable = new Runnable() {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003691 @Override
3692 public void run() {
3693 mEmptyShadeView.setVisibility(GONE);
3694 mEmptyShadeView.setWillBeGone(false);
3695 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003696 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003697 }
Selim Cinek20867102014-12-10 17:09:17 +01003698 };
Selim Cineka9d3efd2016-08-16 14:16:59 -07003699 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek20867102014-12-10 17:09:17 +01003700 mEmptyShadeView.setWillBeGone(true);
3701 mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
3702 } else {
3703 mEmptyShadeView.setInvisible();
3704 onFinishedRunnable.run();
3705 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003706 }
3707 }
3708 }
3709
Dan Sandlereceda3d2014-07-21 15:35:01 -04003710 public void updateDismissView(boolean visible) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003711 int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003712 int newVisibility = visible ? VISIBLE : GONE;
3713 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003714 if (newVisibility != GONE) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003715 if (mDismissView.willBeGone()) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003716 mDismissView.cancelAnimation();
3717 } else {
3718 mDismissView.setInvisible();
Dan Sandlereceda3d2014-07-21 15:35:01 -04003719 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003720 mDismissView.setVisibility(newVisibility);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003721 mDismissView.setWillBeGone(false);
3722 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02003723 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003724 } else {
Selim Cinek7d5f3742014-11-07 18:07:49 +01003725 Runnable dimissHideFinishRunnable = new Runnable() {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003726 @Override
3727 public void run() {
3728 mDismissView.setVisibility(GONE);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003729 mDismissView.setWillBeGone(false);
3730 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02003731 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003732 }
Selim Cinek7d5f3742014-11-07 18:07:49 +01003733 };
Selim Cinek20867102014-12-10 17:09:17 +01003734 if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
Selim Cinek7d5f3742014-11-07 18:07:49 +01003735 mDismissView.setWillBeGone(true);
3736 mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
3737 } else {
3738 dimissHideFinishRunnable.run();
Selim Cinek7d5f3742014-11-07 18:07:49 +01003739 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003740 }
3741 }
3742 }
3743
3744 public void setDismissAllInProgress(boolean dismissAllInProgress) {
3745 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07003746 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07003747 handleDismissAllClipping();
3748 }
3749
3750 private void handleDismissAllClipping() {
3751 final int count = getChildCount();
3752 boolean previousChildWillBeDismissed = false;
3753 for (int i = 0; i < count; i++) {
3754 ExpandableView child = (ExpandableView) getChildAt(i);
3755 if (child.getVisibility() == GONE) {
3756 continue;
3757 }
3758 if (mDismissAllInProgress && previousChildWillBeDismissed) {
3759 child.setMinClipTopAmount(child.getClipTopAmount());
3760 } else {
3761 child.setMinClipTopAmount(0);
3762 }
3763 previousChildWillBeDismissed = canChildBeDismissed(child);
3764 }
Selim Cineka272dfe2015-02-20 18:12:28 +01003765 }
3766
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003767 public boolean isDismissViewNotGone() {
3768 return mDismissView.getVisibility() != View.GONE && !mDismissView.willBeGone();
3769 }
3770
3771 public boolean isDismissViewVisible() {
3772 return mDismissView.isVisible();
3773 }
3774
3775 public int getDismissViewHeight() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08003776 return mDismissView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02003777 }
3778
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01003779 public int getEmptyShadeViewHeight() {
3780 return mEmptyShadeView.getHeight();
3781 }
3782
Jorim Jaggie0640dd2014-08-05 23:12:40 +02003783 public float getBottomMostNotificationBottom() {
3784 final int count = getChildCount();
3785 float max = 0;
3786 for (int childIdx = 0; childIdx < count; childIdx++) {
3787 ExpandableView child = (ExpandableView) getChildAt(childIdx);
3788 if (child.getVisibility() == GONE) {
3789 continue;
3790 }
Selim Cineka686b2c2016-10-26 13:58:27 -07003791 float bottom = child.getTranslationY() + child.getActualHeight()
3792 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02003793 if (bottom > max) {
3794 max = bottom;
3795 }
3796 }
Selim Cinekd2281152015-04-10 14:37:46 -07003797 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02003798 }
3799
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003800 public void setStatusBar(StatusBar statusBar) {
3801 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02003802 }
3803
Selim Cinekb5605e52015-02-20 18:21:41 +01003804 public void setGroupManager(NotificationGroupManager groupManager) {
3805 this.mGroupManager = groupManager;
3806 }
3807
Selim Cinekd9acca52014-09-01 22:33:25 +02003808 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01003809 requestAnimateEverything();
3810 }
3811
3812 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02003813 if (mIsExpanded && mAnimationsEnabled) {
3814 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01003815 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02003816 requestChildrenUpdate();
3817 }
3818 }
3819
Selim Cinek04fb2582015-06-02 19:58:09 +02003820 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01003821 int childCount = getChildCount();
3822 for (int i = childCount - 1; i >= 0; i--) {
3823 ExpandableView child = (ExpandableView) getChildAt(i);
3824 if (child.getVisibility() != View.GONE) {
3825 float childTop = child.getY();
3826 if (childTop > touchY) {
3827 // we are above a notification entirely let's abort
3828 return false;
3829 }
Selim Cineka686b2c2016-10-26 13:58:27 -07003830 boolean belowChild = touchY > childTop + child.getActualHeight()
3831 - child.getClipBottomAmount();
Selim Cinekabf60bb2015-02-20 17:36:10 +01003832 if (child == mDismissView) {
3833 if(!belowChild && !mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
3834 touchY - childTop)) {
3835 // We clicked on the dismiss button
3836 return false;
3837 }
3838 } else if (child == mEmptyShadeView) {
3839 // We arrived at the empty shade view, for which we accept all clicks
3840 return true;
3841 } else if (!belowChild){
3842 // We are on a child
3843 return false;
3844 }
3845 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003846 }
Selim Cinek04fb2582015-06-02 19:58:09 +02003847 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003848 }
3849
Selim Cinekb5605e52015-02-20 18:21:41 +01003850 @Override
3851 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07003852 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
3853 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01003854 if (animated) {
3855 mExpandedGroupView = changedRow;
3856 mNeedsAnimation = true;
3857 }
3858 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07003859 if (!mGroupExpandedForMeasure) {
3860 onHeightChanged(changedRow, false /* needsAnimation */);
3861 }
Mady Mellorb0a82462016-04-30 17:31:02 -07003862 runAfterAnimationFinished(new Runnable() {
3863 @Override
3864 public void run() {
3865 changedRow.onFinishedExpansionChange();
3866 }
3867 });
Selim Cinekb5605e52015-02-20 18:21:41 +01003868 }
3869
3870 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003871 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003872 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08003873 }
3874
Selim Cinekc22fff62016-05-20 12:44:30 -07003875 /** @hide */
3876 @Override
3877 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
3878 super.onInitializeAccessibilityEventInternal(event);
3879 event.setScrollable(mScrollable);
3880 event.setScrollX(mScrollX);
3881 event.setScrollY(mOwnScrollY);
3882 event.setMaxScrollX(mScrollX);
3883 event.setMaxScrollY(getScrollRange());
3884 }
3885
3886 @Override
3887 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
3888 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07003889 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07003890 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07003891 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07003892 info.addAction(
3893 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
3894 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
3895 }
Selim Cinekef406062016-09-29 17:33:13 -07003896 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07003897 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
3898 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
3899 }
3900 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07003901 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
3902 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07003903 }
3904
3905 /** @hide */
3906 @Override
3907 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
3908 if (super.performAccessibilityActionInternal(action, arguments)) {
3909 return true;
3910 }
3911 if (!isEnabled()) {
3912 return false;
3913 }
3914 int direction = -1;
3915 switch (action) {
3916 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
3917 // fall through
3918 case android.R.id.accessibilityActionScrollDown:
3919 direction = 1;
3920 // fall through
3921 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
3922 // fall through
3923 case android.R.id.accessibilityActionScrollUp:
3924 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07003925 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07003926 final int targetScrollY = Math.max(0,
3927 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
3928 if (targetScrollY != mOwnScrollY) {
3929 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
3930 postInvalidateOnAnimation();
3931 return true;
3932 }
3933 break;
3934 }
3935 return false;
3936 }
3937
Selim Cinekef5127e2015-12-21 16:55:58 -08003938 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07003939 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003940 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01003941 }
3942
3943 public void generateChildOrderChangedEvent() {
3944 if (mIsExpanded && mAnimationsEnabled) {
3945 mGenerateChildOrderChangedEvent = true;
3946 mNeedsAnimation = true;
3947 requestChildrenUpdate();
3948 }
3949 }
3950
Selim Cinek684a4422015-04-15 16:18:39 -07003951 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003952 mAnimationFinishedRunnables.add(runnable);
3953 }
3954
3955 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
3956 mHeadsUpManager = headsUpManager;
3957 mAmbientState.setHeadsUpManager(headsUpManager);
3958 }
3959
3960 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
3961 if (mAnimationsEnabled) {
3962 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
3963 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07003964 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003965 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07003966 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003967 requestChildrenUpdate();
3968 }
3969 }
3970
3971 public void setShadeExpanded(boolean shadeExpanded) {
3972 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07003973 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003974 }
3975
Selim Cineka59ecc32015-04-07 10:51:49 -07003976 /**
3977 * Set the boundary for the bottom heads up position. The heads up will always be above this
3978 * position.
3979 *
3980 * @param height the height of the screen
3981 * @param bottomBarHeight the height of the bar on the bottom
3982 */
3983 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
3984 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
3985 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003986 requestChildrenUpdate();
3987 }
3988
3989 public void setTrackingHeadsUp(boolean trackingHeadsUp) {
3990 mTrackingHeadsUp = trackingHeadsUp;
3991 }
3992
Selim Cinekaac93252015-04-14 20:04:12 -07003993 public void setScrimController(ScrimController scrimController) {
3994 mScrimController = scrimController;
Selim Cinekd35c2792016-01-21 13:20:57 -08003995 mScrimController.setScrimBehindChangeRunnable(new Runnable() {
3996 @Override
3997 public void run() {
3998 updateBackgroundDimming();
3999 }
4000 });
Selim Cinekaac93252015-04-14 20:04:12 -07004001 }
4002
Selim Cinekbbc580b2015-06-03 14:11:03 +02004003 public void forceNoOverlappingRendering(boolean force) {
4004 mForceNoOverlappingRendering = force;
4005 }
4006
4007 @Override
4008 public boolean hasOverlappingRendering() {
4009 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4010 }
4011
Selim Cinek6811d722016-01-19 17:53:12 -08004012 public void setAnimationRunning(boolean animationRunning) {
4013 if (animationRunning != mAnimationRunning) {
4014 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004015 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004016 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004017 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004018 }
4019 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004020 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004021 }
4022 }
4023
Selim Cinek3776fe02016-02-04 13:32:43 -08004024 public boolean isExpanded() {
4025 return mIsExpanded;
4026 }
4027
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004028 public void setPulsing(boolean pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004029 if (mPulsing == pulsing) {
4030 return;
4031 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004032 mPulsing = pulsing;
4033 updateNotificationAnimationStates();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004034 updateContentHeight();
4035 notifyHeightChangeListener(mShelf);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004036 }
4037
Selim Cinek07304f5222016-05-19 18:31:36 -07004038 public void setFadingOut(boolean fadingOut) {
4039 if (fadingOut != mFadingOut) {
4040 mFadingOut = fadingOut;
4041 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004042 }
4043 }
4044
Selim Cinek48ff9b42016-11-09 19:31:51 -08004045 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4046 if (mScrimController == null) {
4047 // we're not set up yet.
4048 return;
4049 }
4050 if (parentNotFullyVisible != mParentNotFullyVisible) {
4051 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004052 updateFadingState();
4053 }
4054 }
4055
4056 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004057 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004058 updateSrcDrawing();
4059 }
4060
Selim Cinek31d37b92016-04-26 09:56:42 -07004061 @Override
4062 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4063 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004064 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004065 }
4066
Selim Cinek3afd00e2014-08-11 22:32:57 +02004067 /**
Selim Cinek7103fd42016-05-09 22:22:33 -04004068 * Remove the a given view from the viewstate. This is currently used when the children are
4069 * kept in the parent artificially to have a nicer animation.
4070 * @param view the view to remove
4071 */
4072 public void removeViewStateForView(View view) {
4073 mCurrentStackScrollState.removeViewStateForView(view);
4074 }
4075
Selim Cinekbc243a92016-09-27 16:35:13 -07004076 public void setQsExpanded(boolean qsExpanded) {
4077 mQsExpanded = qsExpanded;
4078 updateAlgorithmLayoutMinHeight();
4079 }
4080
Selim Cinekef406062016-09-29 17:33:13 -07004081 public void setOwnScrollY(int ownScrollY) {
4082 if (ownScrollY != mOwnScrollY) {
4083 mOwnScrollY = ownScrollY;
4084 updateForwardAndBackwardScrollability();
4085 }
4086 }
4087
Selim Cinek281c2022016-10-13 19:14:43 -07004088 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004089 int index = -1;
4090 if (mShelf != null) {
4091 index = indexOfChild(mShelf);
4092 removeView(mShelf);
4093 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004094 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004095 addView(mShelf, index);
4096 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004097 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004098 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004099 }
4100
4101 public NotificationShelf getNotificationShelf() {
4102 return mShelf;
4103 }
4104
Selim Cinekad7fac02016-10-18 17:09:15 -07004105 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4106 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4107 mMaxDisplayedNotifications = maxDisplayedNotifications;
4108 updateContentHeight();
4109 notifyHeightChangeListener(mShelf);
4110 }
4111 }
4112
Selim Cinek48ff9b42016-11-09 19:31:51 -08004113 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004114 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004115 }
4116
Selim Cinekcafa87f2016-10-26 17:00:17 -07004117 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4118 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4119 updateClipping();
4120 }
4121
4122 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4123 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4124 updateClipping();
4125 }
4126
Selim Cinek355652a2016-12-07 13:32:12 -08004127 public void setStatusBarState(int statusBarState) {
4128 mStatusBarState = statusBarState;
4129 mAmbientState.setStatusBarState(statusBarState);
4130 }
4131
Selim Cinekd5ab6452016-12-08 16:34:00 -08004132 public void setExpandingVelocity(float expandingVelocity) {
4133 mAmbientState.setExpandingVelocity(expandingVelocity);
4134 }
4135
Selim Cinekfcff4c62016-12-27 14:26:06 +01004136 public float getOpeningHeight() {
4137 if (mEmptyShadeView.getVisibility() == GONE) {
4138 return getMinExpansionHeight();
4139 } else {
4140 return getAppearEndPosition();
4141 }
4142 }
4143
4144 public void setIsFullWidth(boolean isFullWidth) {
4145 mAmbientState.setPanelFullWidth(isFullWidth);
4146 }
4147
Selim Cinek7103fd42016-05-09 22:22:33 -04004148 /**
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004149 * A listener that is notified when some child locations might have changed.
4150 */
4151 public interface OnChildLocationsChangedListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004152 void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout);
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004153 }
Selim Cinek572bbd42014-04-25 16:43:27 +02004154
Jorim Jaggi290600a2014-05-30 17:02:20 +02004155 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004156 * A listener that is notified when the empty space below the notifications is clicked on
4157 */
4158 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004159 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004160 }
4161
4162 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004163 * A listener that gets notified when the overscroll at the top has changed.
4164 */
4165 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004166
4167 /**
4168 * Notifies a listener that the overscroll has changed.
4169 *
4170 * @param amount the amount of overscroll, in pixels
4171 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4172 * unrubberbanded motion to directly expand overscroll view (e.g expand
4173 * QS)
4174 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004175 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004176
4177 /**
4178 * Notify a listener that the scroller wants to escape from the scrolling motion and
4179 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4180 *
4181 * @param velocity The velocity that the Scroller had when over flinging
4182 * @param open Should the fling open or close the overscroll view.
4183 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004184 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004185 }
4186
Mady Mellor4b80b102016-01-22 08:03:58 -08004187 private class NotificationSwipeHelper extends SwipeHelper {
Mady Mellor97c8df42016-03-22 18:09:39 -07004188 private static final long SHOW_GEAR_DELAY = 60;
4189 private static final long COVER_GEAR_DELAY = 4000;
Mady Mellor5b3cc112016-09-12 15:53:35 -07004190 private static final long SWIPE_GEAR_TIMING = 200;
Mady Mellor4b80b102016-01-22 08:03:58 -08004191 private CheckForDrag mCheckForDrag;
Mady Mellor97c8df42016-03-22 18:09:39 -07004192 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004193 private Handler mHandler;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004194 private boolean mGearSnappedTo;
4195 private boolean mGearSnappedOnLeft;
Mady Mellor4b80b102016-01-22 08:03:58 -08004196
4197 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4198 super(swipeDirection, callback, context);
4199 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004200 mFalsingCheck = new Runnable() {
4201 @Override
4202 public void run() {
4203 resetExposedGearView(true /* animate */, true /* force */);
4204 }
4205 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004206 }
4207
4208 @Override
4209 public void onDownUpdate(View currView) {
4210 // Set the active view
4211 mTranslatingParentView = currView;
4212
4213 // Reset check for drag gesture
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004214 cancelCheckForDrag();
4215 if (mCurrIconRow != null) {
4216 mCurrIconRow.setSnapping(false);
4217 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004218 mCheckForDrag = null;
Mady Mellor34958fa2016-02-23 09:52:17 -08004219 mCurrIconRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004220 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004221
4222 // Slide back any notifications that might be showing a gear
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004223 resetExposedGearView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004224
4225 if (currView instanceof ExpandableNotificationRow) {
4226 // Set the listener for the current row's gear
4227 mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow();
Mady Mellor761cde12017-01-10 11:36:39 -08004228 mCurrIconRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor4b80b102016-01-22 08:03:58 -08004229 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004230 }
4231
4232 @Override
4233 public void onMoveUpdate(View view, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004234 mHandler.removeCallbacks(mFalsingCheck);
4235
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004236 if (mCurrIconRow != null) {
4237 mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
4238
4239 // If the gear is visible and the movement is towards it it's not a location change.
Mady Mellor761cde12017-01-10 11:36:39 -08004240 boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isMenuOnLeft();
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004241 boolean locationChange = isTowardsGear(translation, onLeft)
Mady Mellor761cde12017-01-10 11:36:39 -08004242 ? false : mCurrIconRow.isMenuLocationChange(translation);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004243 if (locationChange) {
4244 // Don't consider it "snapped" if location has changed.
4245 setSnappedToGear(false);
4246
4247 // Changed directions, make sure we check to fade in icon again.
4248 if (!mHandler.hasCallbacks(mCheckForDrag)) {
4249 // No check scheduled, set null to schedule a new one.
4250 mCheckForDrag = null;
4251 } else {
4252 // Check scheduled, reset alpha and update location; check will fade it in
Mady Mellor761cde12017-01-10 11:36:39 -08004253 mCurrIconRow.setMenuAlpha(0f);
4254 mCurrIconRow.setMenuLocation((int) translation);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004255 }
4256 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004257 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004258
Mady Mellor34958fa2016-02-23 09:52:17 -08004259 final boolean gutsExposed = (view instanceof ExpandableNotificationRow)
4260 && ((ExpandableNotificationRow) view).areGutsExposed();
4261
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004262 if (mShouldShowGear && !isPinnedHeadsUp(view) && !gutsExposed) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004263 // Only show the gear if we're not a heads up view and guts aren't exposed.
4264 checkForDrag();
Mady Mellor4b80b102016-01-22 08:03:58 -08004265 }
4266 }
4267
4268 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004269 public void dismissChild(final View view, float velocity,
4270 boolean useAccelerateInterpolator) {
4271 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004272 if (mIsExpanded) {
4273 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4274 // of the panel early.
4275 handleChildDismissed(view);
4276 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004277 handleGearCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004278 }
4279
4280 @Override
4281 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004282 super.snapChild(animView, targetLeft, velocity);
4283 onDragCancelled(animView);
4284 if (targetLeft == 0) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004285 handleGearCoveredOrDismissed();
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004286 }
4287 }
4288
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004289 private void handleGearCoveredOrDismissed() {
4290 cancelCheckForDrag();
4291 setSnappedToGear(false);
4292 if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
4293 mGearExposedView = null;
4294 }
4295 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004296
4297 @Override
4298 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4299 float translation) {
4300 if (mCurrIconRow == null) {
4301 cancelCheckForDrag();
4302 return false; // Let SwipeHelper handle it.
4303 }
4304
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004305 // If the gear icon should not be shown, then there is no need to check if the a swipe
4306 // should result in a snapping to the gear icon. As a result, just check if the swipe
4307 // was enough to dismiss the notification.
4308 if (!mShouldShowGear) {
4309 dismissOrSnapBack(animView, velocity, ev);
4310 return true;
4311 }
4312
Mady Mellor761cde12017-01-10 11:36:39 -08004313 boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isMenuOnLeft());
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004314 boolean gestureFastEnough = Math.abs(velocity) > getEscapeVelocity();
Mady Mellor5b3cc112016-09-12 15:53:35 -07004315 final double timeForGesture = ev.getEventTime() - ev.getDownTime();
4316 final boolean showGearForSlowOnGoing = !canChildBeDismissed(animView)
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004317 && timeForGesture >= SWIPE_GEAR_TIMING;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004318
4319 if (mGearSnappedTo && mCurrIconRow.isVisible()) {
Mady Mellor761cde12017-01-10 11:36:39 -08004320 if (mGearSnappedOnLeft == mCurrIconRow.isMenuOnLeft()) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004321 boolean coveringGear =
4322 Math.abs(getTranslation(animView)) <= getSpaceForGear(animView) * 0.6f;
4323 if (gestureTowardsGear || coveringGear) {
4324 // Gesture is towards or covering the gear
4325 snapChild(animView, 0 /* leftTarget */, velocity);
4326 } else if (isDismissGesture(ev)) {
4327 // Gesture is a dismiss that's not towards the gear
Mady Mellordc6c97d2016-03-31 14:18:35 -07004328 dismissChild(animView, velocity,
4329 !swipedFastEnough() /* useAccelerateInterpolator */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004330 } else {
4331 // Didn't move enough to dismiss or cover, snap to the gear
4332 snapToGear(animView, velocity);
4333 }
4334 } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
4335 || (gestureTowardsGear && !swipedFarEnough())) {
4336 // The gear has been snapped to previously, however, the gear is now on the
4337 // other side. If gesture is towards gear and not too far snap to the gear.
4338 snapToGear(animView, velocity);
4339 } else {
4340 dismissOrSnapBack(animView, velocity, ev);
4341 }
Mady Mellor5b3cc112016-09-12 15:53:35 -07004342 } else if (((!gestureFastEnough || showGearForSlowOnGoing)
4343 && swipedEnoughToShowGear(animView))
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004344 || gestureTowardsGear) {
4345 // Gear has not been snapped to previously and this is gear revealing gesture
4346 snapToGear(animView, velocity);
4347 } else {
4348 dismissOrSnapBack(animView, velocity, ev);
4349 }
4350 return true;
4351 }
4352
4353 private void dismissOrSnapBack(View animView, float velocity, MotionEvent ev) {
4354 if (isDismissGesture(ev)) {
Mady Mellordc6c97d2016-03-31 14:18:35 -07004355 dismissChild(animView, velocity,
4356 !swipedFastEnough() /* useAccelerateInterpolator */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004357 } else {
4358 snapChild(animView, 0 /* leftTarget */, velocity);
4359 }
4360 }
4361
4362 private void snapToGear(View animView, float velocity) {
4363 final float snapBackThreshold = getSpaceForGear(animView);
Mady Mellor761cde12017-01-10 11:36:39 -08004364 final float target = mCurrIconRow.isMenuOnLeft() ? snapBackThreshold
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004365 : -snapBackThreshold;
4366 mGearExposedView = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004367 if (animView instanceof ExpandableNotificationRow) {
4368 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
4369 ((ExpandableNotificationRow) animView).getStatusBarNotification()
4370 .getPackageName());
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004371 }
4372 if (mCurrIconRow != null) {
4373 mCurrIconRow.setSnapping(true);
4374 setSnappedToGear(true);
4375 }
4376 onDragCancelled(animView);
Mady Mellor97c8df42016-03-22 18:09:39 -07004377
4378 // If we're on the lockscreen we want to false this.
Selim Cinekbc243a92016-09-27 16:35:13 -07004379 if (isAntiFalsingNeeded()) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004380 mHandler.removeCallbacks(mFalsingCheck);
4381 mHandler.postDelayed(mFalsingCheck, COVER_GEAR_DELAY);
4382 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004383 super.snapChild(animView, target, velocity);
4384 }
4385
4386 private boolean swipedEnoughToShowGear(View animView) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004387 if (mTranslatingParentView == null) {
4388 return false;
4389 }
Mady Mellorfcba9592016-03-23 11:55:53 -07004390 // If the notification can't be dismissed then how far it can move is
4391 // restricted -- reduce the distance it needs to move in this case.
4392 final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
4393 final float snapBackThreshold = getSpaceForGear(animView) * multiplier;
Mady Mellor4b80b102016-01-22 08:03:58 -08004394 final float translation = getTranslation(animView);
Mady Mellor761cde12017-01-10 11:36:39 -08004395 return !swipedFarEnough() && mCurrIconRow.isVisible() && (mCurrIconRow.isMenuOnLeft()
Mady Mellor5b3cc112016-09-12 15:53:35 -07004396 ? translation > snapBackThreshold
4397 : translation < -snapBackThreshold);
Mady Mellor4b80b102016-01-22 08:03:58 -08004398 }
4399
4400 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004401 public Animator getViewTranslationAnimator(View v, float target,
4402 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004403 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004404 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4405 } else {
4406 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004407 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004408 }
4409
4410 @Override
4411 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004412 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004413 }
4414
4415 @Override
4416 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004417 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004418 }
4419
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004420 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004421 NotificationGuts guts = mStatusBar.getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004422 View view = null;
4423 int height = 0;
4424 if (guts != null) {
4425 // Checking guts
4426 view = guts;
4427 height = guts.getActualHeight();
4428 } else if (mCurrIconRow != null && mCurrIconRow.isVisible()
4429 && mTranslatingParentView != null) {
4430 // Checking gear
4431 view = mTranslatingParentView;
4432 height = ((ExpandableView) mTranslatingParentView).getActualHeight();
4433 }
4434 if (view != null) {
4435 final int rx = (int) ev.getRawX();
4436 final int ry = (int) ev.getRawY();
4437
Selim Cinekfc7086e2016-09-22 14:03:41 -07004438 view.getLocationOnScreen(mTempInt2);
4439 final int x = mTempInt2[0];
4440 final int y = mTempInt2[1];
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004441 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
Selim Cinekfc7086e2016-09-22 14:03:41 -07004442 if (!rect.contains(rx, ry)) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004443 // Touch was outside visible guts / gear notification, close what's visible
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004444 mStatusBar.dismissPopups(-1, -1, true /* resetGear */, true /* animate */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004445 }
4446 }
4447 }
4448
Mady Mellor4b80b102016-01-22 08:03:58 -08004449 /**
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004450 * Returns whether the gesture is towards the gear location or not.
4451 */
4452 private boolean isTowardsGear(float velocity, boolean onLeft) {
4453 if (mCurrIconRow == null) {
4454 return false;
4455 }
4456 return mCurrIconRow.isVisible()
4457 && ((onLeft && velocity <= 0) || (!onLeft && velocity >= 0));
4458 }
4459
4460 /**
4461 * Indicates the the gear has been snapped to.
4462 */
4463 private void setSnappedToGear(boolean snapped) {
Mady Mellor761cde12017-01-10 11:36:39 -08004464 mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isMenuOnLeft() : false;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004465 mGearSnappedTo = snapped && mCurrIconRow != null;
4466 }
4467
4468 /**
Mady Mellor4b80b102016-01-22 08:03:58 -08004469 * Returns the horizontal space in pixels required to display the gear behind a
4470 * notification.
4471 */
4472 private float getSpaceForGear(View view) {
4473 if (view instanceof ExpandableNotificationRow) {
4474 return ((ExpandableNotificationRow) view).getSpaceForGear();
4475 }
4476 return 0;
4477 }
4478
4479 private void checkForDrag() {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004480 if (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag)) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004481 mCheckForDrag = new CheckForDrag();
Mady Mellor97c8df42016-03-22 18:09:39 -07004482 mHandler.postDelayed(mCheckForDrag, SHOW_GEAR_DELAY);
Mady Mellor4b80b102016-01-22 08:03:58 -08004483 }
4484 }
4485
4486 private void cancelCheckForDrag() {
4487 if (mCurrIconRow != null) {
4488 mCurrIconRow.cancelFadeAnimator();
4489 }
4490 mHandler.removeCallbacks(mCheckForDrag);
Mady Mellor4b80b102016-01-22 08:03:58 -08004491 }
4492
4493 private final class CheckForDrag implements Runnable {
4494 @Override
4495 public void run() {
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004496 if (mTranslatingParentView == null) {
4497 return;
4498 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004499 final float translation = getTranslation(mTranslatingParentView);
4500 final float absTransX = Math.abs(translation);
4501 final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView);
4502 final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004503 if ((mCurrIconRow != null && (!mCurrIconRow.isVisible()
Mady Mellor761cde12017-01-10 11:36:39 -08004504 || mCurrIconRow.isMenuLocationChange(translation)))
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004505 && absTransX >= bounceBackToGearWidth * 0.4
Mady Mellor4b80b102016-01-22 08:03:58 -08004506 && absTransX < notiThreshold) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004507 // Fade in the gear
Mady Mellor761cde12017-01-10 11:36:39 -08004508 mCurrIconRow.fadeInMenu(translation > 0 /* fromLeft */, translation,
Mady Mellor4b80b102016-01-22 08:03:58 -08004509 notiThreshold);
Mady Mellor4b80b102016-01-22 08:03:58 -08004510 }
4511 }
4512 }
4513
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004514 public void resetExposedGearView(boolean animate, boolean force) {
4515 if (mGearExposedView == null
4516 || (!force && mGearExposedView == mTranslatingParentView)) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004517 // If no gear is showing or it's showing for this view we do nothing.
4518 return;
4519 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004520 final View prevGearExposedView = mGearExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004521 if (animate) {
4522 Animator anim = getViewTranslationAnimator(prevGearExposedView,
4523 0 /* leftTarget */, null /* updateListener */);
4524 if (anim != null) {
4525 anim.start();
4526 }
4527 } else if (mGearExposedView instanceof ExpandableNotificationRow) {
4528 ((ExpandableNotificationRow) mGearExposedView).resetTranslation();
4529 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004530 mGearExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004531 mGearSnappedTo = false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004532 }
4533 }
4534
Selim Cinek33223572016-02-19 19:32:22 -08004535 private void updateContinuousShadowDrawing() {
4536 boolean continuousShadowUpdate = mAnimationRunning
4537 || !mAmbientState.getDraggedViews().isEmpty();
4538 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4539 if (continuousShadowUpdate) {
4540 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4541 } else {
4542 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4543 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004544 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004545 }
4546 }
4547
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004548 public void resetExposedGearView(boolean animate, boolean force) {
4549 mSwipeHelper.resetExposedGearView(animate, force);
4550 }
4551
4552 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4553 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4554 }
4555
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004556 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004557
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004558 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4559
4560 // ANIMATION_TYPE_ADD
4561 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004562 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004563 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004564 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004565 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004566 .animateZ()
4567 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004568
4569 // ANIMATION_TYPE_REMOVE
4570 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004571 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004572 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004573 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004574 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004575 .animateZ()
4576 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004577
4578 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4579 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004580 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004581 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004582 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004583 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004584 .animateZ()
4585 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004586
4587 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4588 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004589 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004590 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004591 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004592 .animateY()
4593 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004594 .animateZ(),
4595
4596 // ANIMATION_TYPE_START_DRAG
4597 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004598 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004599
4600 // ANIMATION_TYPE_SNAP_BACK
4601 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004602 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004603 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004604
4605 // ANIMATION_TYPE_ACTIVATED_CHILD
4606 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004607 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004608
4609 // ANIMATION_TYPE_DIMMED
4610 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004611 .animateDimmed(),
4612
4613 // ANIMATION_TYPE_CHANGE_POSITION
4614 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004615 .animateAlpha() // maybe the children change positions
4616 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004617 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004618 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004619 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004620 .animateZ(),
4621
4622 // ANIMATION_TYPE_DARK
4623 new AnimationFilter()
Jorim Jaggi4e857f42014-11-17 19:14:04 +01004624 .animateDark()
4625 .hasDelays(),
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004626
4627 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4628 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004629 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004630 .animateHeight()
4631 .animateTopInset()
4632 .animateY()
4633 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004634 .animateZ()
4635 .hasDelays(),
4636
4637 // ANIMATION_TYPE_HIDE_SENSITIVE
4638 new AnimationFilter()
4639 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02004640
4641 // ANIMATION_TYPE_VIEW_RESIZE
4642 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004643 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02004644 .animateHeight()
4645 .animateTopInset()
4646 .animateY()
4647 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02004648
Selim Cinekb5605e52015-02-20 18:21:41 +01004649 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
4650 new AnimationFilter()
4651 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004652 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01004653 .animateHeight()
4654 .animateTopInset()
4655 .animateY()
4656 .animateZ(),
4657
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004658 // ANIMATION_TYPE_HEADS_UP_APPEAR
4659 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004660 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004661 .animateHeight()
4662 .animateTopInset()
4663 .animateY()
4664 .animateZ(),
4665
4666 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4667 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004668 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004669 .animateHeight()
4670 .animateTopInset()
4671 .animateY()
4672 .animateZ(),
4673
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004674 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4675 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004676 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004677 .animateHeight()
4678 .animateTopInset()
4679 .animateY()
4680 .animateZ()
4681 .hasDelays(),
4682
Selim Cineka59ecc32015-04-07 10:51:49 -07004683 // ANIMATION_TYPE_HEADS_UP_OTHER
4684 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004685 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07004686 .animateHeight()
4687 .animateTopInset()
4688 .animateY()
4689 .animateZ(),
4690
Selim Cinekd9acca52014-09-01 22:33:25 +02004691 // ANIMATION_TYPE_EVERYTHING
4692 new AnimationFilter()
4693 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004694 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02004695 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02004696 .animateDimmed()
4697 .animateHideSensitive()
4698 .animateHeight()
4699 .animateTopInset()
4700 .animateY()
4701 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004702 };
4703
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004704 static int[] LENGTHS = new int[] {
4705
4706 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004707 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004708
4709 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004710 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004711
4712 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4713 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4714
4715 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4716 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4717
4718 // ANIMATION_TYPE_START_DRAG
4719 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4720
4721 // ANIMATION_TYPE_SNAP_BACK
4722 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4723
4724 // ANIMATION_TYPE_ACTIVATED_CHILD
4725 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
4726
4727 // ANIMATION_TYPE_DIMMED
4728 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004729
4730 // ANIMATION_TYPE_CHANGE_POSITION
4731 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04004732
4733 // ANIMATION_TYPE_DARK
4734 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004735
4736 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4737 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02004738
4739 // ANIMATION_TYPE_HIDE_SENSITIVE
4740 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02004741
4742 // ANIMATION_TYPE_VIEW_RESIZE
4743 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02004744
Selim Cinekb5605e52015-02-20 18:21:41 +01004745 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08004746 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01004747
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004748 // ANIMATION_TYPE_HEADS_UP_APPEAR
4749 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
4750
4751 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4752 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4753
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004754 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4755 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4756
Selim Cineka59ecc32015-04-07 10:51:49 -07004757 // ANIMATION_TYPE_HEADS_UP_OTHER
4758 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4759
Selim Cinekd9acca52014-09-01 22:33:25 +02004760 // ANIMATION_TYPE_EVERYTHING
4761 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004762 };
4763
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004764 static final int ANIMATION_TYPE_ADD = 0;
4765 static final int ANIMATION_TYPE_REMOVE = 1;
4766 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
4767 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
4768 static final int ANIMATION_TYPE_START_DRAG = 4;
4769 static final int ANIMATION_TYPE_SNAP_BACK = 5;
4770 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
4771 static final int ANIMATION_TYPE_DIMMED = 7;
4772 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04004773 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004774 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02004775 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02004776 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01004777 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004778 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
4779 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004780 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
4781 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
4782 static final int ANIMATION_TYPE_EVERYTHING = 18;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004783
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004784 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
4785 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
4786
Selim Cinek572bbd42014-04-25 16:43:27 +02004787 final long eventStartTime;
4788 final View changingView;
4789 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004790 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004791 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004792 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004793 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07004794 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02004795
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004796 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004797 this(view, type, LENGTHS[type]);
4798 }
4799
4800 AnimationEvent(View view, int type, long length) {
Selim Cinek572bbd42014-04-25 16:43:27 +02004801 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
4802 changingView = view;
4803 animationType = type;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004804 filter = FILTERS[type];
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02004805 this.length = length;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004806 }
4807
4808 /**
4809 * Combines the length of several animation events into a single value.
4810 *
4811 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004812 * @return The combined length. Depending on the event types, this might be the maximum of
4813 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004814 */
4815 static long combineLength(ArrayList<AnimationEvent> events) {
4816 long length = 0;
4817 int size = events.size();
4818 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004819 AnimationEvent event = events.get(i);
4820 length = Math.max(length, event.length);
4821 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
4822 return event.length;
4823 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004824 }
4825 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02004826 }
4827 }
Selim Cinek67b22602014-03-10 15:40:16 +01004828}