blob: 174e73476695e6d93a791cb69493e0f1d1ffae3d [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 Cinek2627d722018-01-19 12:16:49 -080019import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
20
Selim Cinek614576e2016-01-20 10:54:09 -080021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.ObjectAnimator;
24import android.animation.PropertyValuesHolder;
Selim Cinekd35c2792016-01-21 13:20:57 -080025import android.animation.TimeAnimator;
26import android.animation.ValueAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080027import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinek31d37b92016-04-26 09:56:42 -070028import android.annotation.FloatRange;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010029import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010030import android.content.Context;
31import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080032import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010033import android.graphics.Canvas;
Lucas Dupind285cf02018-01-18 09:18:23 -080034import android.graphics.Color;
Selim Cinek67b22602014-03-10 15:40:16 +010035import android.graphics.Paint;
Selim Cinek515b2032017-11-15 10:20:19 -080036import android.graphics.Path;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010037import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080038import android.graphics.PorterDuff;
39import android.graphics.PorterDuffXfermode;
40import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070041import android.os.Bundle;
Mady Mellor4b80b102016-01-22 08:03:58 -080042import android.os.Handler;
Mady Mellor95d743c2017-01-10 12:05:27 -080043import android.service.notification.StatusBarNotification;
Anthony Chen5e3742e2017-04-07 14:28:44 -070044import android.support.annotation.NonNull;
Selim Cinek8a9308d2017-08-24 09:31:08 -070045import android.support.annotation.VisibleForTesting;
Selim Cinek67b22602014-03-10 15:40:16 +010046import android.util.AttributeSet;
Selim Cinek972123d2016-05-03 14:25:58 -070047import android.util.FloatProperty;
Selim Cinek67b22602014-03-10 15:40:16 +010048import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080049import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070050import android.util.Pair;
Selim Cinek972123d2016-05-03 14:25:58 -070051import android.util.Property;
Lucas Dupine17ce522017-07-17 15:45:06 -070052import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070053import android.view.InputDevice;
Selim Cinek67b22602014-03-10 15:40:16 +010054import android.view.MotionEvent;
55import android.view.VelocityTracker;
56import android.view.View;
57import android.view.ViewConfiguration;
58import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020059import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070060import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070061import android.view.accessibility.AccessibilityEvent;
62import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020063import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080064import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010065import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070066import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070067
Mady Mellora41587b2016-02-11 18:43:06 -080068import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010069import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Lucas Dupine17ce522017-07-17 15:45:06 -070070import com.android.settingslib.Utils;
Selim Cinek67b22602014-03-10 15:40:16 +010071import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080072import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010073import com.android.systemui.R;
74import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070075import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080076import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
77import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
78import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Selim Cineka32ab602014-06-11 15:06:01 +020079import com.android.systemui.statusbar.ActivatableNotificationView;
Dan Sandlereceda3d2014-07-21 15:35:01 -040080import com.android.systemui.statusbar.DismissView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020081import com.android.systemui.statusbar.EmptyShadeView;
Selim Cinek67b22602014-03-10 15:40:16 +010082import com.android.systemui.statusbar.ExpandableNotificationRow;
Jorim Jaggibe565df2014-04-28 17:51:23 +020083import com.android.systemui.statusbar.ExpandableView;
Adrian Roos7d062c42017-03-30 15:11:43 -070084import com.android.systemui.statusbar.NotificationData;
Mady Mellor7a9b2a62016-03-23 07:41:47 -070085import com.android.systemui.statusbar.NotificationGuts;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090086import com.android.systemui.statusbar.NotificationListContainer;
87import com.android.systemui.statusbar.NotificationLogger;
Selim Cinek0cfbef42016-11-09 19:06:36 -080088import com.android.systemui.statusbar.NotificationShelf;
Mady Mellorc2dbe492017-03-30 13:22:03 -070089import com.android.systemui.statusbar.NotificationSnooze;
Selim Cinek3a9c10a2014-10-28 14:21:10 +010090import com.android.systemui.statusbar.StackScrollerDecorView;
Selim Cinekcb2b6732014-09-05 16:17:22 +020091import com.android.systemui.statusbar.StatusBarState;
Selim Cinek33223572016-02-19 19:32:22 -080092import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinek42357e02016-02-24 18:48:01 -080093import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080094import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090095import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Selim Cinekb5605e52015-02-20 18:21:41 +010096import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinekaac93252015-04-14 20:04:12 -070097import com.android.systemui.statusbar.phone.ScrimController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080098import com.android.systemui.statusbar.phone.StatusBar;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090099import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100100import com.android.systemui.statusbar.policy.ScrollAdapter;
Selim Cinek67b22602014-03-10 15:40:16 +0100101
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700102import android.support.v4.graphics.ColorUtils;
103
Selim Cinek707e2072017-06-30 18:32:40 +0200104import java.io.FileDescriptor;
105import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200106import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800107import java.util.Collections;
108import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200109import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800110import java.util.List;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800111import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200112
Selim Cinek67b22602014-03-10 15:40:16 +0100113/**
114 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
115 */
116public class NotificationStackScrollLayout extends ViewGroup
Jorim Jaggibe565df2014-04-28 17:51:23 +0200117 implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
Mady Mellor4b80b102016-01-22 08:03:58 -0800118 ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900119 NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
120 NotificationListContainer {
Selim Cinek67b22602014-03-10 15:40:16 +0100121
Selim Cinekd35c2792016-01-21 13:20:57 -0800122 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800123 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100124 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200125 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
126 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200127 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100128 /**
129 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
130 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800131 private static final int INVALID_POINTER = -1;
Selim Cinek67b22602014-03-10 15:40:16 +0100132
Selim Cinek1408eb52014-06-02 14:45:38 +0200133 private ExpandHelper mExpandHelper;
Selim Cinek33223572016-02-19 19:32:22 -0800134 private NotificationSwipeHelper mSwipeHelper;
Selim Cinek343e6e22014-04-11 21:23:30 +0200135 private boolean mSwipingInProgress;
Selim Cinek67b22602014-03-10 15:40:16 +0100136 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800137 private final Paint mBackgroundPaint = new Paint();
Selim Cinek515b2032017-11-15 10:20:19 -0800138 private final Path mBackgroundPath = new Path();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700139 private final boolean mShouldDrawNotificationBackground;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100140
Selim Cinekbc243a92016-09-27 16:35:13 -0700141 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100142 private int mOwnScrollY;
143 private int mMaxLayoutHeight;
144
145 private VelocityTracker mVelocityTracker;
146 private OverScroller mScroller;
Ricky Waicd35def2016-05-03 11:07:07 +0100147 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100148 private int mTouchSlop;
149 private int mMinimumVelocity;
150 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100151 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200152 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100153 private boolean mIsBeingDragged;
154 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200155 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700156 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100157 private boolean mTouchIsClick;
158 private float mInitialTouchX;
159 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100160
Selim Cinek67b22602014-03-10 15:40:16 +0100161 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100162 private int mContentHeight;
163 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100164 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800165 private int mIncreasedPaddingBetweenElements;
Lucas Dupind285cf02018-01-18 09:18:23 -0800166 private int mRegularTopPadding;
167 private int mDarkTopPadding;
168 // Current padding, will be either mRegularTopPadding or mDarkTopPadding
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200169 private int mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800170 // Distance between AOD separator and shelf
171 private int mDarkSeparatorPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700172 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700173 private int mBottomInset = 0;
Selim Cinek67b22602014-03-10 15:40:16 +0100174
175 /**
176 * The algorithm which calculates the properties for our children
177 */
Muyuan Li87798022016-04-07 17:51:25 -0700178 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100179
180 /**
181 * The current State this Layout is in
182 */
Selim Cinek572bbd42014-04-25 16:43:27 +0200183 private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
Selim Cinek281c2022016-10-13 19:14:43 -0700184 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100185 private NotificationGroupManager mGroupManager;
Selim Cinek3776fe02016-02-04 13:32:43 -0800186 private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700187 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
188 private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
189 private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
190 private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
191 private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200192 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700193 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
194 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200195 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200196 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200197 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800198 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200199
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200200 /**
201 * The raw amount of the overScroll on the top, which is not rubber-banded.
202 */
203 private float mOverScrolledTopPixels;
204
205 /**
206 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
207 */
208 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900209 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200210 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200211 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100212 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200213 private boolean mNeedsAnimation;
214 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200215 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200216 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400217 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100218 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200219 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200220 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200221 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200222 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200223 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700224 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200225 private boolean mExpandingNotification;
226 private boolean mExpandedInThisMotion;
Muyuan Li84b45612016-06-01 11:05:08 -0700227 protected boolean mScrollingEnabled;
Muyuan Li8cf63dc2016-05-18 17:40:58 -0700228 protected DismissView mDismissView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700229 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400230 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700231 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200232
233 /**
234 * Was the scroller scrolled to the top when the down motion was observed?
235 */
236 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200237 /**
238 * The minimal amount of over scroll which is needed in order to switch to the quick settings
239 * when over scrolling on a expanded card.
240 */
241 private float mMinTopOverScrollToEscape;
242 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700243 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200244 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200245 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700246 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200247 private boolean mNeedViewResizeAnimation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100248 private View mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700249 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700250
Selim Cinek1408eb52014-06-02 14:45:38 +0200251 /**
252 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
253 * This is needed to avoid scrolling too far after the notification was collapsed in the same
254 * motion.
255 */
256 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000257 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellor4b80b102016-01-22 08:03:58 -0800258
Mady Mellor95d743c2017-01-10 12:05:27 -0800259 private NotificationMenuRowPlugin mCurrMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -0800260 private View mTranslatingParentView;
Mady Mellor95d743c2017-01-10 12:05:27 -0800261 private View mMenuExposedView;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700262 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200263
264 /**
265 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
266 * animating.
267 */
268 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700269 private boolean mDisallowDismissInThisMotion;
Jorim Jaggi56306252014-07-03 00:40:09 +0200270 private boolean mInterceptDelegateEnabled;
271 private boolean mDelegateToScrollView;
Selim Cineka59ecc32015-04-07 10:51:49 -0700272 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700273 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200274 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200275 = new ViewTreeObserver.OnPreDrawListener() {
276 @Override
277 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400278 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200279 updateChildren();
280 mChildrenUpdateRequested = false;
281 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200282 return true;
283 }
284 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500285 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100286 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100287 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700288 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek0fccc722015-07-29 17:04:36 -0700289 private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700290 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
291 = new HashSet<>();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900292 private HeadsUpManagerPhone mHeadsUpManager;
Selim Cinek29aab962018-02-27 17:05:45 -0800293 private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700294 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700295 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200296 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700297 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700298 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800299 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700300 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800301 = new ViewTreeObserver.OnPreDrawListener() {
302 @Override
303 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700304 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800305 return true;
306 }
307 };
308 private Rect mBackgroundBounds = new Rect();
Selim Cinek614576e2016-01-20 10:54:09 -0800309 private Rect mStartAnimationRect = new Rect();
310 private Rect mEndAnimationRect = new Rect();
Selim Cinekd35c2792016-01-21 13:20:57 -0800311 private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
Selim Cinek614576e2016-01-20 10:54:09 -0800312 private boolean mAnimateNextBackgroundBottom;
313 private boolean mAnimateNextBackgroundTop;
314 private ObjectAnimator mBottomAnimator = null;
315 private ObjectAnimator mTopAnimator = null;
316 private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
317 private ActivatableNotificationView mLastVisibleBackgroundChild = null;
Selim Cinekd35c2792016-01-21 13:20:57 -0800318 private int mBgColor;
319 private float mDimAmount;
320 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800321 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Selim Cinekd35c2792016-01-21 13:20:57 -0800322 private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
323 @Override
324 public void onAnimationEnd(Animator animation) {
325 mDimAnimator = null;
326 }
327 };
328 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
329 = new ValueAnimator.AnimatorUpdateListener() {
330
331 @Override
332 public void onAnimationUpdate(ValueAnimator animation) {
333 setDimAmount((Float) animation.getAnimatedValue());
334 }
335 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700336 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800337 private boolean mContinuousShadowUpdate;
338 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
339 = new ViewTreeObserver.OnPreDrawListener() {
340
341 @Override
342 public boolean onPreDraw() {
343 updateViewShadows();
344 return true;
345 }
346 };
347 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
348 @Override
349 public int compare(ExpandableView view, ExpandableView otherView) {
350 float endY = view.getTranslationY() + view.getActualHeight();
351 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
352 if (endY < otherEndY) {
353 return -1;
354 } else if (endY > otherEndY) {
355 return 1;
356 } else {
357 // The two notifications end at the same location
358 return 0;
359 }
360 }
361 };
Selim Cinek25503252016-03-03 15:31:43 -0800362 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900363 private boolean mPulsing;
Selim Cinek31d37b92016-04-26 09:56:42 -0700364 private boolean mDrawBackgroundAsSrc;
Selim Cinek07304f5222016-05-19 18:31:36 -0700365 private boolean mFadingOut;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800366 private boolean mParentNotFullyVisible;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700367 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700368 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400369 private View mForcedScroll;
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -0800370 private float mDarkAmount = 0f;
Lucas Dupind285cf02018-01-18 09:18:23 -0800371 private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
372 new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
Selim Cinek972123d2016-05-03 14:25:58 -0700373 @Override
374 public void setValue(NotificationStackScrollLayout object, float value) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800375 object.setDarkAmount(value);
Selim Cinek972123d2016-05-03 14:25:58 -0700376 }
377
378 @Override
379 public Float get(NotificationStackScrollLayout object) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800380 return object.getDarkAmount();
Selim Cinek972123d2016-05-03 14:25:58 -0700381 }
382 };
Lucas Dupine17ce522017-07-17 15:45:06 -0700383 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700384 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700385 private boolean mForwardScrollable;
386 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700387 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700388 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800389 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700390 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800391 private boolean mNoAmbient;
392 private final Rect mClipRect = new Rect();
393 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700394 private Rect mRequestedClipBounds;
395 private boolean mInHeadsUpPinnedMode;
396 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800397 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100398 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700399 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Selim Cinek9212de82017-02-06 16:04:28 -0800400 private Runnable mAnimateScroll = this::animateScroll;
Selim Cinek0fe07392017-11-09 13:26:34 -0800401 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800402 private int mSidePaddings;
Lucas Dupind285cf02018-01-18 09:18:23 -0800403 private final int mSeparatorWidth;
404 private final int mSeparatorThickness;
405 private final Rect mTmpRect = new Rect();
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800406 private int mClockBottom;
Lucas Dupin0cd882f2018-01-30 12:19:49 -0800407 private int mAntiBurnInOffsetX;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800408 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
409 private int mHeadsUpInset;
Selim Cinek67b22602014-03-10 15:40:16 +0100410
411 public NotificationStackScrollLayout(Context context) {
412 this(context, null);
413 }
414
415 public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
416 this(context, attrs, 0);
417 }
418
419 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
420 this(context, attrs, defStyleAttr, 0);
421 }
422
423 public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
424 int defStyleRes) {
425 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800426 Resources res = getResources();
427
Selim Cinek281c2022016-10-13 19:14:43 -0700428 mAmbientState = new AmbientState(context);
Selim Cinekd35c2792016-01-21 13:20:57 -0800429 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800430 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
431 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Selim Cinek1cf41c12014-08-12 20:06:19 +0200432 mExpandHelper = new ExpandHelper(getContext(), this,
433 minHeight, maxHeight);
434 mExpandHelper.setEventSource(this);
435 mExpandHelper.setScrollAdapter(this);
Mady Mellor4b80b102016-01-22 08:03:58 -0800436 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
Muyuan Li333a4fc2016-04-16 17:13:46 -0700437 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100438 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800439 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800440 mShouldDrawNotificationBackground =
441 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700442 mFadeNotificationsOnDismiss =
443 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Lucas Dupind285cf02018-01-18 09:18:23 -0800444 mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
445 mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
446 mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
Selim Cinek29aab962018-02-27 17:05:45 -0800447 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
448 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800449 addOnExpandedHeightListener(mRoundnessManager::setExpanded);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800450
451 updateWillNotDraw();
Lucas Dupind285cf02018-01-18 09:18:23 -0800452 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100453 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100454 mDebugPaint = new Paint();
455 mDebugPaint.setColor(0xffff0000);
456 mDebugPaint.setStrokeWidth(2);
457 mDebugPaint.setStyle(Paint.Style.STROKE);
458 }
459 }
460
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900461 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800462 public NotificationSwipeActionHelper getSwipeActionHelper() {
463 return mSwipeHelper;
464 }
465
Selim Cinek67b22602014-03-10 15:40:16 +0100466 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800467 public void onMenuClicked(View view, int x, int y, MenuItem item) {
468 if (mLongPressListener == null) {
469 return;
470 }
471 if (view instanceof ExpandableNotificationRow) {
472 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Mady Mellora41587b2016-02-11 18:43:06 -0800473 MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
474 row.getStatusBarNotification().getPackageName());
Mady Mellor4b80b102016-01-22 08:03:58 -0800475 }
Mady Mellor43c2cd12016-12-12 21:05:13 -0800476 mLongPressListener.onLongPress(view, x, y, item);
Mady Mellor4b80b102016-01-22 08:03:58 -0800477 }
478
479 @Override
Mady Mellor43c2cd12016-12-12 21:05:13 -0800480 public void onMenuReset(View row) {
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700481 if (mTranslatingParentView != null && row == mTranslatingParentView) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800482 mMenuExposedView = null;
Mady Mellor7a9b2a62016-03-23 07:41:47 -0700483 mTranslatingParentView = null;
484 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +0000485 }
486
487 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800488 public void onMenuShown(View row) {
489 mMenuExposedView = mTranslatingParentView;
490 if (row instanceof ExpandableNotificationRow) {
491 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
492 ((ExpandableNotificationRow) row).getStatusBarNotification()
493 .getPackageName());
494 }
495 mSwipeHelper.onMenuShown(row);
496 }
497
Selim Cinek67b22602014-03-10 15:40:16 +0100498 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800499 if (mShouldDrawNotificationBackground
500 && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
501 drawBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700502 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800503
Selim Cinek67b22602014-03-10 15:40:16 +0100504 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800505 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100506 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800507 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100508 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200509 y = getHeight() - getEmptyBottomMargin();
510 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100511 }
512 }
513
Lucas Dupind285cf02018-01-18 09:18:23 -0800514 private void drawBackground(Canvas canvas) {
515 final int lockScreenLeft = mSidePaddings;
516 final int lockScreenRight = getWidth() - mSidePaddings;
517 final int lockScreenTop = mCurrentBounds.top;
518 final int lockScreenBottom = mCurrentBounds.bottom;
519 final int darkLeft = getWidth() / 2 - mSeparatorWidth / 2;
520 final int darkRight = darkLeft + mSeparatorWidth;
521 final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
522 final int darkBottom = darkTop + mSeparatorThickness;
523
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800524 if (mAmbientState.hasPulsingNotifications()) {
525 // TODO draw divider between notification and shelf
526 } else if (mAmbientState.isDark()) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800527 // Only draw divider on AOD if we actually have notifications
528 if (mFirstVisibleBackgroundChild != null) {
529 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
530 }
531 setClipBounds(null);
532 } else {
533 float animProgress = Interpolators.FAST_OUT_SLOW_IN
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -0800534 .getInterpolation(1f - mDarkAmount);
Lucas Dupind285cf02018-01-18 09:18:23 -0800535 float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -0800536 .getInterpolation((1f - mDarkAmount) * 2);
Lucas Dupind285cf02018-01-18 09:18:23 -0800537 mTmpRect.set((int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
538 (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
539 (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
540 (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress));
541 canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
542 mCornerRadius, mCornerRadius, mBackgroundPaint);
543 setClipBounds(animProgress == 1 ? null : mTmpRect);
544 }
545 }
546
Selim Cinekd35c2792016-01-21 13:20:57 -0800547 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800548 // No need to update the background color if it's not being drawn.
549 if (!mShouldDrawNotificationBackground) {
550 return;
551 }
552
Lucas Dupind285cf02018-01-18 09:18:23 -0800553 final int color;
554 if (mAmbientState.isDark()) {
555 color = Color.WHITE;
556 } else {
557 float alpha =
558 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -0800559 alpha *= 1f - mDarkAmount;
Lucas Dupind285cf02018-01-18 09:18:23 -0800560 // We need to manually blend in the background color
561 int scrimColor = mScrimController.getBackgroundColor();
562 color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
563 }
564
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100565 if (mCachedBackgroundColor != color) {
566 mCachedBackgroundColor = color;
567 mBackgroundPaint.setColor(color);
568 invalidate();
569 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800570 }
571
Selim Cinek67b22602014-03-10 15:40:16 +0100572 private void initView(Context context) {
573 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100574 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200575 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100576 final ViewConfiguration configuration = ViewConfiguration.get(context);
577 mTouchSlop = configuration.getScaledTouchSlop();
578 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
579 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100580 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700581
582 Resources res = context.getResources();
583 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800584 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700585 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700586 mPaddingBetweenElements = Math.max(1,
587 res.getDimensionPixelSize(R.dimen.notification_divider_height));
588 mIncreasedPaddingBetweenElements =
589 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
590 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200591 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800592 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700593 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800594 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700595 mMinInteractionHeight = res.getDimensionPixelSize(
596 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800597 mCornerRadius = res.getDimensionPixelSize(
598 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800599 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
600 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200601 }
602
Selim Cinek25503252016-03-03 15:31:43 -0800603 public void setDrawBackgroundAsSrc(boolean asSrc) {
Selim Cinek31d37b92016-04-26 09:56:42 -0700604 mDrawBackgroundAsSrc = asSrc;
605 updateSrcDrawing();
606 }
607
608 private void updateSrcDrawing() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800609 if (!mShouldDrawNotificationBackground) {
610 return;
611 }
612
Selim Cinek48ff9b42016-11-09 19:31:51 -0800613 mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
Selim Cinek07304f5222016-05-19 18:31:36 -0700614 ? mSrcMode : null);
Selim Cinek25503252016-03-03 15:31:43 -0800615 invalidate();
616 }
617
Selim Cinekaef92ef2014-06-06 18:06:04 +0200618 private void notifyHeightChangeListener(ExpandableView view) {
619 if (mOnHeightChangedListener != null) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100620 mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200621 }
Selim Cinek67b22602014-03-10 15:40:16 +0100622 }
623
624 @Override
625 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
626 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -0800627
628 int width = MeasureSpec.getSize(widthMeasureSpec);
629 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
630 MeasureSpec.getMode(widthMeasureSpec));
Selim Cinekfa760d42016-05-10 15:50:53 -0400631 // We need to measure all children even the GONE ones, such that the heights are calculated
632 // correctly as they are used to calculate how many we can fit on the screen.
633 final int size = getChildCount();
634 for (int i = 0; i < size; i++) {
Selim Cinekb95fd182017-12-21 13:03:32 -0800635 measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400636 }
Selim Cinek67b22602014-03-10 15:40:16 +0100637 }
638
639 @Override
640 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100641 // we layout all our children centered on the top
642 float centerX = getWidth() / 2.0f;
643 for (int i = 0; i < getChildCount(); i++) {
644 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400645 // We need to layout all children even the GONE ones, such that the heights are
646 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100647 float width = child.getMeasuredWidth();
648 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100649 child.layout((int) (centerX - width / 2.0f),
650 0,
651 (int) (centerX + width / 2.0f),
652 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100653 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200654 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100655 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200656 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200657 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800658 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700659 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100660 }
661
Selim Cinek5bc852a2015-12-21 12:19:09 -0800662 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
663 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200664 mNeedViewResizeAnimation = true;
665 mNeedsAnimation = true;
666 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200667 }
668
Selim Cinekdb167372016-11-17 15:41:17 -0800669 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
670 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800671 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200672 }
673
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900674 @Override
675 public void setChildLocationsChangedListener(
676 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200677 mListener = listener;
678 }
679
Selim Cineka7d4f822016-12-06 14:34:47 -0800680 @Override
681 public boolean isInVisibleLocation(ExpandableNotificationRow row) {
682 ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200683 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800684 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200685 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100686 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800687 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +0100688 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800689 if (row.getVisibility() != View.VISIBLE) {
690 return false;
691 }
692 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200693 }
694
Selim Cinek67b22602014-03-10 15:40:16 +0100695 private void setMaxLayoutHeight(int maxLayoutHeight) {
696 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -0700697 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200698 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +0100699 }
700
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200701 private void updateAlgorithmHeightAndPadding() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900702 if (mPulsing) {
Lucas Dupin6bf7b642018-01-22 18:56:24 -0800703 mTopPadding = mClockBottom;
704 } else {
705 mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
706 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700707 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -0700708 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700709 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +0100710 }
711
Selim Cinekbc243a92016-09-27 16:35:13 -0700712 private void updateAlgorithmLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800713 mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
714 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -0700715 }
716
Selim Cinek67b22602014-03-10 15:40:16 +0100717 /**
718 * Updates the children views according to the stack scroll algorithm. Call this whenever
719 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
720 */
721 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -0800722 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -0800723 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
724 ? 0
725 : mScroller.getCurrVelocity());
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200726 mAmbientState.setScrollY(mOwnScrollY);
727 mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200728 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +0200729 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +0100730 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +0200731 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +0100732 }
733 }
734
Selim Cinekc383fd02016-10-21 15:31:26 -0700735 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700736 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800737 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -0700738 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
739 updateBackground();
740 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700741 }
742
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800743 private void updateClippingToTopRoundedCorner() {
Selim Cinek2627d722018-01-19 12:16:49 -0800744 Float clipStart = (float) mTopPadding + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800745 Float clipEnd = clipStart + mCornerRadius;
746 boolean first = true;
747 for (int i = 0; i < getChildCount(); i++) {
748 ExpandableView child = (ExpandableView) getChildAt(i);
749 if (child.getVisibility() == GONE) {
750 continue;
751 }
752 float start = child.getTranslationY();
753 float end = start + Math.max(child.getActualHeight() - child.getClipBottomAmount(),
754 0);
755 boolean clip = clipStart > start && clipStart < end
756 || clipEnd >= start && clipEnd <= end;
757 clip &= !(first && mOwnScrollY == 0);
758 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : -1);
759 first = false;
760 }
761 }
762
Selim Cinek3776fe02016-02-04 13:32:43 -0800763 private void updateScrollStateForAddedChildren() {
764 if (mChildrenToAddAnimated.isEmpty()) {
765 return;
766 }
767 for (int i = 0; i < getChildCount(); i++) {
768 ExpandableView child = (ExpandableView) getChildAt(i);
769 if (mChildrenToAddAnimated.contains(child)) {
770 int startingPosition = getPositionInLinearLayout(child);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800771 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
772 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
773 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
Selim Cinek3776fe02016-02-04 13:32:43 -0800774 int childHeight = getIntrinsicHeight(child) + padding;
775 if (startingPosition < mOwnScrollY) {
776 // This child starts off screen, so let's keep it offscreen to keep the others visible
777
Selim Cinekef406062016-09-29 17:33:13 -0700778 setOwnScrollY(mOwnScrollY + childHeight);
Selim Cinek3776fe02016-02-04 13:32:43 -0800779 }
780 }
781 }
782 clampScrollPosition();
783 }
784
Adrian Roos181385c2016-05-05 17:45:44 -0400785 private void updateForcedScroll() {
786 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
787 || !mForcedScroll.isAttachedToWindow())) {
788 mForcedScroll = null;
789 }
790 if (mForcedScroll != null) {
791 ExpandableView expandableView = (ExpandableView) mForcedScroll;
792 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
793 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -0700794 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400795
796 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -0700797
798 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
799 // that it is not visible anymore.
800 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700801 setOwnScrollY(targetScroll);
Adrian Roos181385c2016-05-05 17:45:44 -0400802 }
803 }
804 }
805
Selim Cinek319bdc42014-05-01 23:01:58 +0200806 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +0200807 if (!mChildrenUpdateRequested) {
808 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
809 mChildrenUpdateRequested = true;
810 invalidate();
811 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200812 }
813
Selim Cinek67b22602014-03-10 15:40:16 +0100814 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +0200815 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +0100816 }
817
Selim Cinekf7a14c02014-07-07 14:01:46 +0200818 private void clampScrollPosition() {
Selim Cinek67b22602014-03-10 15:40:16 +0100819 int scrollRange = getScrollRange();
820 if (scrollRange < mOwnScrollY) {
Selim Cinekef406062016-09-29 17:33:13 -0700821 setOwnScrollY(scrollRange);
Selim Cinek67b22602014-03-10 15:40:16 +0100822 }
823 }
824
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200825 public int getTopPadding() {
826 return mTopPadding;
827 }
828
Selim Cinek1408eb52014-06-02 14:45:38 +0200829 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800830 if (mRegularTopPadding != topPadding) {
831 mRegularTopPadding = topPadding;
832 mDarkTopPadding = topPadding + mDarkSeparatorPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200833 updateAlgorithmHeightAndPadding();
834 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200835 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200836 mTopPaddingNeedsAnimation = true;
837 mNeedsAnimation = true;
838 }
Selim Cinek319bdc42014-05-01 23:01:58 +0200839 requestChildrenUpdate();
Selim Cinekaef92ef2014-06-06 18:06:04 +0200840 notifyHeightChangeListener(null);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200841 }
842 }
843
844 /**
Selim Cinekbc243a92016-09-27 16:35:13 -0700845 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200846 *
Selim Cinekbc243a92016-09-27 16:35:13 -0700847 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200848 */
Selim Cinekbc243a92016-09-27 16:35:13 -0700849 public void setExpandedHeight(float height) {
850 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700851 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800852 int minExpansionHeight = getMinExpansionHeight();
853 if (height < minExpansionHeight) {
854 mClipRect.left = 0;
855 mClipRect.right = getWidth();
856 mClipRect.top = 0;
857 mClipRect.bottom = (int) height;
858 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700859 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800860 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -0700861 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800862 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200863 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -0700864 float translationY;
865 float appearEndPosition = getAppearEndPosition();
866 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800867 float appearFraction = 1.0f;
Selim Cinek94c2d822016-07-13 18:50:04 -0700868 if (height >= appearEndPosition) {
Selim Cinekbc243a92016-09-27 16:35:13 -0700869 translationY = 0;
Selim Cinek94c2d822016-07-13 18:50:04 -0700870 stackHeight = (int) height;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200871 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800872 appearFraction = getAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -0700873 if (appearFraction >= 0) {
874 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
875 appearFraction);
876 } else {
877 // This may happen when pushing up a heads up. We linearly push it up from the
878 // start
879 translationY = height - appearStartPosition + getExpandTranslationStart();
880 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800881 if (isHeadsUpTransition()) {
882 stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
883 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
884 } else {
885 stackHeight = (int) (height - translationY);
886 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200887 }
888 if (stackHeight != mCurrentStackHeight) {
889 mCurrentStackHeight = stackHeight;
890 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +0200891 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200892 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700893 setStackTranslation(translationY);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800894 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
895 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
896 listener.accept(mExpandedHeight, appearFraction);
897 }
Selim Cinekcafa87f2016-10-26 17:00:17 -0700898 }
899
900 private void setRequestedClipBounds(Rect clipRect) {
901 mRequestedClipBounds = clipRect;
902 updateClipping();
903 }
904
905 public void updateClipping() {
906 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
907 && !mHeadsUpAnimatingAway;
908 if (mIsClipped != clipped) {
909 mIsClipped = clipped;
910 updateFadingState();
911 }
912 if (clipped) {
913 setClipBounds(mRequestedClipBounds);
914 } else {
915 setClipBounds(null);
916 }
Selim Cinek94c2d822016-07-13 18:50:04 -0700917 }
918
919 /**
920 * @return The translation at the beginning when expanding.
921 * Measured relative to the resting position.
922 */
923 private float getExpandTranslationStart() {
Selim Cinek48ff9b42016-11-09 19:31:51 -0800924 return - mTopPadding;
Selim Cinek94c2d822016-07-13 18:50:04 -0700925 }
926
927 /**
928 * @return the position from where the appear transition starts when expanding.
929 * Measured in absolute height.
930 */
931 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800932 if (isHeadsUpTransition()) {
933 return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -0700934 }
Selim Cinek48ff9b42016-11-09 19:31:51 -0800935 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -0700936 }
937
938 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900939 * @return the height of the top heads up notification when pinned. This is different from the
940 * intrinsic height, which also includes whether the notification is system expanded and
941 * is mainly used when dragging down from a heads up notification.
942 */
943 private int getTopHeadsUpPinnedHeight() {
944 NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
945 if (topEntry == null) {
946 return 0;
947 }
948 ExpandableNotificationRow row = topEntry.row;
949 if (row.isChildInGroup()) {
950 final ExpandableNotificationRow groupSummary
951 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
952 if (groupSummary != null) {
953 row = groupSummary;
954 }
955 }
956 return row.getPinnedHeadsUpHeight();
957 }
958
959 /**
Selim Cinek94c2d822016-07-13 18:50:04 -0700960 * @return the position from where the appear transition ends when expanding.
961 * Measured in absolute height.
962 */
963 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -0700964 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -0700965 int notGoneChildCount = getNotGoneChildCount();
966 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800967 if (isHeadsUpTransition()
Selim Cinekebf42342017-07-13 15:46:10 +0200968 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900969 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +0100970 } else {
971 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800972 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
973 appearPosition += mShelf.getIntrinsicHeight();
974 }
Selim Cinekcde90e52016-12-22 21:01:49 +0100975 }
Selim Cinekaa417da2016-10-27 18:17:08 -0700976 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +0100977 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -0700978 }
979 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -0700980 }
981
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800982 private boolean isHeadsUpTransition() {
983 return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
984 && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
985 }
986
Selim Cinek94c2d822016-07-13 18:50:04 -0700987 /**
988 * @param height the height of the panel
989 * @return the fraction of the appear animation that has been performed
990 */
991 public float getAppearFraction(float height) {
992 float appearEndPosition = getAppearEndPosition();
993 float appearStartPosition = getAppearStartPosition();
994 return (height - appearStartPosition)
995 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700996 }
997
Selim Cinekd2281152015-04-10 14:37:46 -0700998 public float getStackTranslation() {
999 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001000 }
1001
Selim Cinekd2281152015-04-10 14:37:46 -07001002 private void setStackTranslation(float stackTranslation) {
1003 if (stackTranslation != mStackTranslation) {
1004 mStackTranslation = stackTranslation;
1005 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001006 requestChildrenUpdate();
1007 }
Selim Cinek67b22602014-03-10 15:40:16 +01001008 }
1009
1010 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001011 * 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 +01001012 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1013 *
1014 * @return either the layout height or the externally defined height, whichever is smaller
1015 */
Selim Cinek343e6e22014-04-11 21:23:30 +02001016 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001017 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1018 }
1019
Selim Cinek816c8e42015-11-19 12:00:45 -08001020 public int getFirstItemMinHeight() {
1021 final ExpandableView firstChild = getFirstChildNotGone();
1022 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001023 }
1024
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001025 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
Dan Sandler4247a5c2014-07-23 15:58:08 -04001026 mLongPressListener = listener;
Selim Cinek67b22602014-03-10 15:40:16 +01001027 }
1028
Jason Monk16ac3772016-02-10 15:39:21 -05001029 public void setQsContainer(ViewGroup qsContainer) {
1030 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001031 }
1032
Mady Mellorb0a82462016-04-30 17:31:02 -07001033 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001034 public void onChildDismissed(View v) {
Selim Cinekd1395642016-04-28 12:22:42 -07001035 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1036 if (!row.isDismissed()) {
1037 handleChildDismissed(v);
1038 }
1039 ViewGroup transientContainer = row.getTransientContainer();
1040 if (transientContainer != null) {
1041 transientContainer.removeTransientView(v);
1042 }
1043 }
1044
1045 private void handleChildDismissed(View v) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04001046 if (mDismissAllInProgress) {
1047 return;
1048 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001049 setSwipingInProgress(false);
Selim Cinekeb973562014-05-02 17:07:49 +02001050 if (mDragAnimPendingChildren.contains(v)) {
1051 // We start the swipe and finish it in the same frame, we don't want any animation
1052 // for the drag
1053 mDragAnimPendingChildren.remove(v);
1054 }
Selim Cinek572bbd42014-04-25 16:43:27 +02001055 mSwipedOutViews.add(v);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001056 mAmbientState.onDragFinished(v);
Selim Cinek33223572016-02-19 19:32:22 -08001057 updateContinuousShadowDrawing();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001058 if (v instanceof ExpandableNotificationRow) {
1059 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1060 if (row.isHeadsUp()) {
Selim Cinek684a4422015-04-15 16:18:39 -07001061 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001062 }
1063 }
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001064 if (v instanceof ExpandableNotificationRow) {
1065 ((ExpandableNotificationRow) v).performDismiss(false /* fromAccessibility */);
1066 }
Blazej Magnowski72323322015-07-24 11:49:40 -07001067
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001068 mFalsingManager.onNotificationDismissed();
1069 if (mFalsingManager.shouldEnforceBouncer()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001070 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
Felipe Lemeee5d6302016-04-22 16:11:19 -07001071 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
Blazej Magnowski72323322015-07-24 11:49:40 -07001072 }
Selim Cinekeb973562014-05-02 17:07:49 +02001073 }
1074
1075 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08001076 public void onChildSnappedBack(View animView, float targetLeft) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001077 mAmbientState.onDragFinished(animView);
Selim Cinek33223572016-02-19 19:32:22 -08001078 updateContinuousShadowDrawing();
Selim Cinekeb973562014-05-02 17:07:49 +02001079 if (!mDragAnimPendingChildren.contains(animView)) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001080 if (mAnimationsEnabled) {
1081 mSnappedBackChildren.add(animView);
1082 mNeedsAnimation = true;
1083 }
Selim Cinekeb973562014-05-02 17:07:49 +02001084 requestChildrenUpdate();
Selim Cinekeb973562014-05-02 17:07:49 +02001085 } else {
1086 // We start the swipe and snap back in the same frame, we don't want any animation
1087 mDragAnimPendingChildren.remove(animView);
1088 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001089 if (mCurrMenuRow != null && targetLeft == 0) {
1090 mCurrMenuRow.resetMenu();
1091 mCurrMenuRow = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08001092 }
Selim Cinek67b22602014-03-10 15:40:16 +01001093 }
1094
Adrian Roos5d9cc662014-05-28 17:08:13 +02001095 @Override
1096 public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
Selim Cinek131c1e22015-05-11 19:04:49 -07001097 if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
Selim Cinekaac93252015-04-14 20:04:12 -07001098 mScrimController.setTopHeadsUpDragAmount(animView,
Winsonbde852d2016-04-15 19:06:54 -07001099 Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
Selim Cinekaac93252015-04-14 20:04:12 -07001100 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001101 // Returning true prevents alpha fading.
1102 return !mFadeNotificationsOnDismiss;
Adrian Roos5d9cc662014-05-28 17:08:13 +02001103 }
1104
Mady Mellorb0a82462016-04-30 17:31:02 -07001105 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001106 public void onBeginDrag(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001107 mFalsingManager.onNotificatonStartDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001108 setSwipingInProgress(true);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001109 mAmbientState.onBeginDrag(v);
Selim Cinek33223572016-02-19 19:32:22 -08001110 updateContinuousShadowDrawing();
Selim Cinek131c1e22015-05-11 19:04:49 -07001111 if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
Jorim Jaggi75c95042014-05-16 19:09:59 +02001112 mDragAnimPendingChildren.add(v);
1113 mNeedsAnimation = true;
1114 }
Selim Cinekeb973562014-05-02 17:07:49 +02001115 requestChildrenUpdate();
Selim Cinek67b22602014-03-10 15:40:16 +01001116 }
1117
Selim Cinek684a4422015-04-15 16:18:39 -07001118 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001119 if (v instanceof ExpandableNotificationRow) {
1120 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001121 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001122 }
1123 return false;
1124 }
1125
1126 private boolean isHeadsUp(View v) {
1127 if (v instanceof ExpandableNotificationRow) {
1128 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1129 return row.isHeadsUp();
1130 }
1131 return false;
1132 }
1133
Mady Mellorb0a82462016-04-30 17:31:02 -07001134 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001135 public void onDragCancelled(View v) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001136 mFalsingManager.onNotificatonStopDismissing();
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001137 setSwipingInProgress(false);
Selim Cinek67b22602014-03-10 15:40:16 +01001138 }
1139
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001140 @Override
1141 public float getFalsingThresholdFactor() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001142 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001143 }
1144
Mady Mellor4b80b102016-01-22 08:03:58 -08001145 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001146 public View getChildAtPosition(MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001147 View child = getChildAtPosition(ev.getX(), ev.getY());
1148 if (child instanceof ExpandableNotificationRow) {
1149 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1150 ExpandableNotificationRow parent = row.getNotificationParent();
Selim Cinek43d30f02016-03-04 10:51:32 -08001151 if (parent != null && parent.areChildrenExpanded()
Selim Cinek38d429f2016-06-03 11:46:56 -07001152 && (parent.areGutsExposed()
Mady Mellor95d743c2017-01-10 12:05:27 -08001153 || mMenuExposedView == parent
Selim Cinek43d30f02016-03-04 10:51:32 -08001154 || (parent.getNotificationChildren().size() == 1
1155 && parent.isClearable()))) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001156 // In this case the group is expanded and showing the menu for the
Mady Mellor4b80b102016-01-22 08:03:58 -08001157 // group, further interaction should apply to the group, not any
Selim Cinek43d30f02016-03-04 10:51:32 -08001158 // child notifications so we use the parent of the child. We also do the same
1159 // if we only have a single child.
1160 child = parent;
Mady Mellor4b80b102016-01-22 08:03:58 -08001161 }
1162 }
1163 return child;
Selim Cinek67b22602014-03-10 15:40:16 +01001164 }
1165
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001166 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1167 getLocationOnScreen(mTempInt2);
1168 float localTouchY = touchY - mTempInt2[1];
1169
1170 ExpandableView closestChild = null;
1171 float minDist = Float.MAX_VALUE;
1172
1173 // find the view closest to the location, accounting for GONE views
1174 final int count = getChildCount();
1175 for (int childIdx = 0; childIdx < count; childIdx++) {
1176 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1177 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001178 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001179 continue;
1180 }
1181 float childTop = slidingChild.getTranslationY();
1182 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001183 float bottom = childTop + slidingChild.getActualHeight()
1184 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001185
1186 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1187 if (dist < minDist) {
1188 closestChild = slidingChild;
1189 minDist = dist;
1190 }
1191 }
1192 return closestChild;
1193 }
1194
Mady Mellorb0a82462016-04-30 17:31:02 -07001195 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001196 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001197 getLocationOnScreen(mTempInt2);
1198 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek67b22602014-03-10 15:40:16 +01001199 }
1200
Mady Mellorb0a82462016-04-30 17:31:02 -07001201 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001202 public ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001203 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1204
1205 }
1206
1207 /**
1208 * Get the child at a certain screen location.
1209 *
1210 * @param touchX the x coordinate
1211 * @param touchY the y coordinate
1212 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1213 * @return the child at the given location.
1214 */
1215 private ExpandableView getChildAtPosition(float touchX, float touchY,
1216 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001217 // find the view under the pointer, accounting for GONE views
1218 final int count = getChildCount();
1219 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001220 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001221 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001222 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001223 continue;
1224 }
Selim Cinek89faff12014-06-19 16:29:04 -07001225 float childTop = slidingChild.getTranslationY();
1226 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001227 float bottom = childTop + slidingChild.getActualHeight()
1228 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001229
1230 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1231 // camera affordance).
1232 int left = 0;
1233 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001234
Selim Cinek34ed7c02017-09-08 15:03:12 -07001235 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001236 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001237 if (slidingChild instanceof ExpandableNotificationRow) {
1238 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Selim Cinek131c1e22015-05-11 19:04:49 -07001239 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001240 && mHeadsUpManager.getTopEntry().row != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001241 && mGroupManager.getGroupSummary(
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001242 mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
Selim Cinek5bc852a2015-12-21 12:19:09 -08001243 != row) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001244 continue;
1245 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001246 return row.getViewAtPosition(touchY - childTop);
1247 }
Selim Cinek67b22602014-03-10 15:40:16 +01001248 return slidingChild;
1249 }
1250 }
1251 return null;
1252 }
1253
Mady Mellorb0a82462016-04-30 17:31:02 -07001254 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001255 public boolean canChildBeExpanded(View v) {
1256 return v instanceof ExpandableNotificationRow
Selim Cinek8d490d42015-04-10 00:05:50 -07001257 && ((ExpandableNotificationRow) v).isExpandable()
Selim Cinek38d429f2016-06-03 11:46:56 -07001258 && !((ExpandableNotificationRow) v).areGutsExposed()
Selim Cinek166f7c42016-03-03 16:21:37 -08001259 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
Selim Cinek67b22602014-03-10 15:40:16 +01001260 }
1261
Chris Wren6abeeb92016-05-26 14:44:38 -04001262 /* Only ever called as a consequence of an expansion gesture in the shade. */
Mady Mellorb0a82462016-04-30 17:31:02 -07001263 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001264 public void setUserExpandedChild(View v, boolean userExpanded) {
1265 if (v instanceof ExpandableNotificationRow) {
Chris Wren6abeeb92016-05-26 14:44:38 -04001266 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek740c1112016-12-22 16:39:54 +01001267 if (userExpanded && onKeyguard()) {
1268 // Due to a race when locking the screen while touching, a notification may be
1269 // expanded even after we went back to keyguard. An example of this happens if
1270 // you click in the empty space while expanding a group.
1271
1272 // We also need to un-user lock it here, since otherwise the content height
1273 // calculated might be wrong. We also can't invert the two calls since
1274 // un-userlocking it will trigger a layout switch in the content view.
1275 row.setUserLocked(false);
1276 updateContentHeight();
1277 notifyHeightChangeListener(row);
1278 return;
1279 }
Chris Wren6abeeb92016-05-26 14:44:38 -04001280 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
1281 row.onExpandedByGesture(userExpanded);
Selim Cinek67b22602014-03-10 15:40:16 +01001282 }
1283 }
1284
Mady Mellorb0a82462016-04-30 17:31:02 -07001285 @Override
1286 public void setExpansionCancelled(View v) {
1287 if (v instanceof ExpandableNotificationRow) {
1288 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
1289 }
1290 }
1291
1292 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001293 public void setUserLockedChild(View v, boolean userLocked) {
1294 if (v instanceof ExpandableNotificationRow) {
1295 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
1296 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001297 cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02001298 requestDisallowInterceptTouchEvent(true);
1299 }
1300
1301 @Override
1302 public void expansionStateChanged(boolean isExpanding) {
1303 mExpandingNotification = isExpanding;
1304 if (!mExpandedInThisMotion) {
1305 mMaxScrollAfterExpand = mOwnScrollY;
1306 mExpandedInThisMotion = true;
1307 }
1308 }
1309
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001310 @Override
1311 public int getMaxExpandHeight(ExpandableView view) {
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001312 return view.getMaxContentHeight();
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001313 }
1314
Selim Cinek1408eb52014-06-02 14:45:38 +02001315 public void setScrollingEnabled(boolean enable) {
1316 mScrollingEnabled = enable;
1317 }
1318
Adrian Roos181385c2016-05-05 17:45:44 -04001319 public void lockScrollTo(View v) {
1320 if (mForcedScroll == v) {
1321 return;
1322 }
1323 mForcedScroll = v;
1324 scrollTo(v);
1325 }
1326
Ricky Waicd35def2016-05-03 11:07:07 +01001327 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001328 ExpandableView expandableView = (ExpandableView) v;
Adrian Roos4a579672016-05-24 16:54:37 -07001329 int positionInLinearLayout = getPositionInLinearLayout(v);
1330 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1331 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001332
Adrian Roos4a579672016-05-24 16:54:37 -07001333 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1334 // that it is not visible anymore.
1335 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001336 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1337 mDontReportNextOverScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001338 animateScroll();
Ricky Waicd35def2016-05-03 11:07:07 +01001339 return true;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001340 }
Ricky Waicd35def2016-05-03 11:07:07 +01001341 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001342 }
1343
Adrian Roos181385c2016-05-05 17:45:44 -04001344 /**
1345 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
1346 * the IME.
1347 */
1348 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1349 return positionInLinearLayout + v.getIntrinsicHeight() +
1350 getImeInset() - getHeight() + getTopPadding();
1351 }
1352
Adrian Roos5153d4a2016-03-22 10:01:56 -07001353 @Override
1354 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001355 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001356
1357 int range = getScrollRange();
1358 if (mOwnScrollY > range) {
1359 // HACK: We're repeatedly getting staggered insets here while the IME is
1360 // animating away. To work around that we'll wait until things have settled.
1361 removeCallbacks(mReclamp);
1362 postDelayed(mReclamp, 50);
Adrian Roos4a579672016-05-24 16:54:37 -07001363 } else if (mForcedScroll != null) {
1364 // The scroll was requested before we got the actual inset - in case we need
1365 // to scroll up some more do so now.
1366 scrollTo(mForcedScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001367 }
1368 return insets;
1369 }
1370
1371 private Runnable mReclamp = new Runnable() {
1372 @Override
1373 public void run() {
1374 int range = getScrollRange();
1375 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1376 mDontReportNextOverScroll = true;
1377 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001378 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001379 }
1380 };
1381
Selim Cinek1408eb52014-06-02 14:45:38 +02001382 public void setExpandingEnabled(boolean enable) {
1383 mExpandHelper.setEnabled(enable);
1384 }
1385
1386 private boolean isScrollingEnabled() {
1387 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001388 }
1389
Mady Mellorb0a82462016-04-30 17:31:02 -07001390 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001391 public boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001392 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001393 }
1394
Selim Cinek19c8c702014-08-25 22:09:19 +02001395 @Override
1396 public boolean isAntiFalsingNeeded() {
Selim Cinekbc243a92016-09-27 16:35:13 -07001397 return onKeyguard();
1398 }
1399
1400 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001401 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001402 }
1403
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001404 private void setSwipingInProgress(boolean isSwiped) {
1405 mSwipingInProgress = isSwiped;
1406 if(isSwiped) {
1407 requestDisallowInterceptTouchEvent(true);
1408 }
Selim Cinek67b22602014-03-10 15:40:16 +01001409 }
1410
1411 @Override
1412 protected void onConfigurationChanged(Configuration newConfig) {
1413 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001414 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001415 float densityScale = getResources().getDisplayMetrics().density;
1416 mSwipeHelper.setDensityScale(densityScale);
1417 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1418 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1419 initView(getContext());
1420 }
1421
Dan Sandlereceda3d2014-07-21 15:35:01 -04001422 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001423 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1424 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001425 }
1426
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001427 @Override
Mady Mellor86889c22016-04-18 16:37:06 -07001428 public void snapViewIfNeeded(ExpandableNotificationRow child) {
dongwan0605.kim30637e42016-03-02 17:16:47 +09001429 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001430 // If the child is showing the notification menu snap to that
1431 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001432 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001433 }
1434
Selim Cinek67b22602014-03-10 15:40:16 +01001435 @Override
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001436 public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
1437 return this;
1438 }
1439
1440 @Override
Selim Cinek67b22602014-03-10 15:40:16 +01001441 public boolean onTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001442 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1443 || ev.getActionMasked()== MotionEvent.ACTION_UP;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01001444 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02001445 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02001446 if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001447 if (isCancelOrUp) {
1448 mExpandHelper.onlyObserveMovements(false);
1449 }
1450 boolean wasExpandingBefore = mExpandingNotification;
1451 expandWantsIt = mExpandHelper.onTouchEvent(ev);
Selim Cinekf7a14c02014-07-07 14:01:46 +02001452 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
1453 && !mDisallowScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001454 dispatchDownEventToScroller(ev);
1455 }
1456 }
Selim Cinek67b22602014-03-10 15:40:16 +01001457 boolean scrollerWantsIt = false;
Selim Cinek684a4422015-04-15 16:18:39 -07001458 if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
1459 && !mDisallowScrollingInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001460 scrollerWantsIt = onScrollTouch(ev);
1461 }
1462 boolean horizontalSwipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02001463 if (!mIsBeingDragged
1464 && !mExpandingNotification
1465 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07001466 && !mOnlyScrollingInThisMotion
1467 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01001468 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
1469 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07001470
1471 // Check if we need to clear any snooze leavebehinds
Eliot Courtney47098cb2017-10-18 17:30:30 +09001472 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07001473 if (guts != null && !isTouchInView(ev, guts)
1474 && guts.getGutsContent() instanceof NotificationSnooze) {
1475 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
1476 if ((ns.isExpanded() && isCancelOrUp)
1477 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
1478 // If the leavebehind is expanded we clear it on the next up event, otherwise we
1479 // clear it on the next non-horizontal swipe or expand event.
1480 checkSnoozeLeavebehind();
1481 }
1482 }
1483 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
1484 mCheckForLeavebehind = true;
1485 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001486 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
1487 }
1488
1489 private void dispatchDownEventToScroller(MotionEvent ev) {
1490 MotionEvent downEvent = MotionEvent.obtain(ev);
1491 downEvent.setAction(MotionEvent.ACTION_DOWN);
1492 onScrollTouch(downEvent);
1493 downEvent.recycle();
Selim Cinek67b22602014-03-10 15:40:16 +01001494 }
1495
Selim Cinek11e33232016-08-05 15:30:53 -07001496 @Override
1497 public boolean onGenericMotionEvent(MotionEvent event) {
1498 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
1499 || mDisallowScrollingInThisMotion) {
1500 return false;
1501 }
1502 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1503 switch (event.getAction()) {
1504 case MotionEvent.ACTION_SCROLL: {
1505 if (!mIsBeingDragged) {
1506 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
1507 if (vscroll != 0) {
1508 final int delta = (int) (vscroll * getVerticalScrollFactor());
1509 final int range = getScrollRange();
1510 int oldScrollY = mOwnScrollY;
1511 int newScrollY = oldScrollY - delta;
1512 if (newScrollY < 0) {
1513 newScrollY = 0;
1514 } else if (newScrollY > range) {
1515 newScrollY = range;
1516 }
1517 if (newScrollY != oldScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08001518 setOwnScrollY(newScrollY);
Selim Cinek11e33232016-08-05 15:30:53 -07001519 return true;
1520 }
1521 }
1522 }
1523 }
1524 }
1525 }
1526 return super.onGenericMotionEvent(event);
1527 }
1528
Selim Cinek67b22602014-03-10 15:40:16 +01001529 private boolean onScrollTouch(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001530 if (!isScrollingEnabled()) {
1531 return false;
1532 }
Adrian Roosfeb97252016-09-29 17:05:58 -07001533 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
Jason Monk16ac3772016-02-10 15:39:21 -05001534 return false;
1535 }
Adrian Roos181385c2016-05-05 17:45:44 -04001536 mForcedScroll = null;
Selim Cinek67b22602014-03-10 15:40:16 +01001537 initVelocityTrackerIfNotExists();
1538 mVelocityTracker.addMovement(ev);
1539
1540 final int action = ev.getAction();
1541
1542 switch (action & MotionEvent.ACTION_MASK) {
1543 case MotionEvent.ACTION_DOWN: {
Jorim Jaggife6bfa62014-05-07 23:23:18 +02001544 if (getChildCount() == 0 || !isInContentBounds(ev)) {
Selim Cinek67b22602014-03-10 15:40:16 +01001545 return false;
1546 }
1547 boolean isBeingDragged = !mScroller.isFinished();
1548 setIsBeingDragged(isBeingDragged);
Selim Cinek67b22602014-03-10 15:40:16 +01001549 /*
1550 * If being flinged and user touches, stop the fling. isFinished
1551 * will be false if being flinged.
1552 */
1553 if (!mScroller.isFinished()) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001554 mScroller.forceFinished(true);
Selim Cinek67b22602014-03-10 15:40:16 +01001555 }
1556
1557 // Remember where the motion event started
1558 mLastMotionY = (int) ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02001559 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01001560 mActivePointerId = ev.getPointerId(0);
1561 break;
1562 }
1563 case MotionEvent.ACTION_MOVE:
1564 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
1565 if (activePointerIndex == -1) {
1566 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
1567 break;
1568 }
1569
1570 final int y = (int) ev.getY(activePointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02001571 final int x = (int) ev.getX(activePointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01001572 int deltaY = mLastMotionY - y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001573 final int xDiff = Math.abs(x - mDownX);
1574 final int yDiff = Math.abs(deltaY);
1575 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01001576 setIsBeingDragged(true);
1577 if (deltaY > 0) {
1578 deltaY -= mTouchSlop;
1579 } else {
1580 deltaY += mTouchSlop;
1581 }
1582 }
1583 if (mIsBeingDragged) {
1584 // Scroll to follow the motion event
1585 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02001586 int range = getScrollRange();
1587 if (mExpandedInThisMotion) {
1588 range = Math.min(range, mMaxScrollAfterExpand);
1589 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001590
1591 float scrollAmount;
1592 if (deltaY < 0) {
1593 scrollAmount = overScrollDown(deltaY);
1594 } else {
1595 scrollAmount = overScrollUp(deltaY, range);
1596 }
Selim Cinek67b22602014-03-10 15:40:16 +01001597
Selim Cinek9212de82017-02-06 16:04:28 -08001598 // Calling customOverScrollBy will call onCustomOverScrolled, which
1599 // sets the scrolling if applicable.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001600 if (scrollAmount != 0.0f) {
1601 // The scrolling motion could not be compensated with the
1602 // existing overScroll, we have to scroll the view
Selim Cinek9212de82017-02-06 16:04:28 -08001603 customOverScrollBy((int) scrollAmount, mOwnScrollY,
1604 range, getHeight() / 2);
Mady Mellorc2dbe492017-03-30 13:22:03 -07001605 // If we're scrolling, leavebehinds should be dismissed
1606 checkSnoozeLeavebehind();
Selim Cinek67b22602014-03-10 15:40:16 +01001607 }
Selim Cinek67b22602014-03-10 15:40:16 +01001608 }
1609 break;
1610 case MotionEvent.ACTION_UP:
1611 if (mIsBeingDragged) {
1612 final VelocityTracker velocityTracker = mVelocityTracker;
1613 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1614 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
1615
Selim Cinek1408eb52014-06-02 14:45:38 +02001616 if (shouldOverScrollFling(initialVelocity)) {
1617 onOverScrollFling(true, initialVelocity);
1618 } else {
1619 if (getChildCount() > 0) {
1620 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
1621 float currentOverScrollTop = getCurrentOverScrollAmount(true);
1622 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
1623 fling(-initialVelocity);
1624 } else {
1625 onOverScrollFling(false, initialVelocity);
1626 }
1627 } else {
1628 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
1629 getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001630 animateScroll();
Selim Cinek1408eb52014-06-02 14:45:38 +02001631 }
Selim Cinek67b22602014-03-10 15:40:16 +01001632 }
1633 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001634 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001635 mActivePointerId = INVALID_POINTER;
1636 endDrag();
Selim Cinek67b22602014-03-10 15:40:16 +01001637 }
Selim Cinek48e746c2014-06-16 16:01:03 -07001638
Selim Cinek67b22602014-03-10 15:40:16 +01001639 break;
1640 case MotionEvent.ACTION_CANCEL:
1641 if (mIsBeingDragged && getChildCount() > 0) {
1642 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08001643 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01001644 }
1645 mActivePointerId = INVALID_POINTER;
1646 endDrag();
1647 }
1648 break;
1649 case MotionEvent.ACTION_POINTER_DOWN: {
1650 final int index = ev.getActionIndex();
1651 mLastMotionY = (int) ev.getY(index);
Selim Cinek1408eb52014-06-02 14:45:38 +02001652 mDownX = (int) ev.getX(index);
Selim Cinek67b22602014-03-10 15:40:16 +01001653 mActivePointerId = ev.getPointerId(index);
1654 break;
1655 }
1656 case MotionEvent.ACTION_POINTER_UP:
1657 onSecondaryPointerUp(ev);
1658 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
Selim Cinek1408eb52014-06-02 14:45:38 +02001659 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
Selim Cinek67b22602014-03-10 15:40:16 +01001660 break;
1661 }
1662 return true;
1663 }
1664
Muyuan Li84b45612016-06-01 11:05:08 -07001665 protected boolean isInsideQsContainer(MotionEvent ev) {
1666 return ev.getY() < mQsContainer.getBottom();
1667 }
1668
Selim Cinek1408eb52014-06-02 14:45:38 +02001669 private void onOverScrollFling(boolean open, int initialVelocity) {
1670 if (mOverscrollTopChangedListener != null) {
1671 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
1672 }
1673 mDontReportNextOverScroll = true;
1674 setOverScrollAmount(0.0f, true, false);
1675 }
1676
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001677 /**
1678 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1679 *
1680 * @param deltaY The amount to scroll upwards, has to be positive.
1681 * @return The amount of scrolling to be performed by the scroller,
1682 * not handled by the overScroll amount.
1683 */
1684 private float overScrollUp(int deltaY, int range) {
1685 deltaY = Math.max(deltaY, 0);
1686 float currentTopAmount = getCurrentOverScrollAmount(true);
1687 float newTopAmount = currentTopAmount - deltaY;
1688 if (currentTopAmount > 0) {
1689 setOverScrollAmount(newTopAmount, true /* onTop */,
1690 false /* animate */);
1691 }
1692 // Top overScroll might not grab all scrolling motion,
1693 // we have to scroll as well.
1694 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1695 float newScrollY = mOwnScrollY + scrollAmount;
1696 if (newScrollY > range) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001697 if (!mExpandedInThisMotion) {
1698 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1699 // We overScroll on the top
1700 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1701 false /* onTop */,
1702 false /* animate */);
1703 }
Selim Cinekef406062016-09-29 17:33:13 -07001704 setOwnScrollY(range);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001705 scrollAmount = 0.0f;
1706 }
1707 return scrollAmount;
1708 }
1709
1710 /**
1711 * Perform a scroll downward and adapt the overscroll amounts accordingly
1712 *
1713 * @param deltaY The amount to scroll downwards, has to be negative.
1714 * @return The amount of scrolling to be performed by the scroller,
1715 * not handled by the overScroll amount.
1716 */
1717 private float overScrollDown(int deltaY) {
1718 deltaY = Math.min(deltaY, 0);
1719 float currentBottomAmount = getCurrentOverScrollAmount(false);
1720 float newBottomAmount = currentBottomAmount + deltaY;
1721 if (currentBottomAmount > 0) {
1722 setOverScrollAmount(newBottomAmount, false /* onTop */,
1723 false /* animate */);
1724 }
1725 // Bottom overScroll might not grab all scrolling motion,
1726 // we have to scroll as well.
1727 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1728 float newScrollY = mOwnScrollY + scrollAmount;
1729 if (newScrollY < 0) {
1730 float currentTopPixels = getCurrentOverScrolledPixels(true);
1731 // We overScroll on the top
1732 setOverScrolledPixels(currentTopPixels - newScrollY,
1733 true /* onTop */,
1734 false /* animate */);
Selim Cinekef406062016-09-29 17:33:13 -07001735 setOwnScrollY(0);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001736 scrollAmount = 0.0f;
1737 }
1738 return scrollAmount;
1739 }
1740
Selim Cinek67b22602014-03-10 15:40:16 +01001741 private void onSecondaryPointerUp(MotionEvent ev) {
1742 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
1743 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
1744 final int pointerId = ev.getPointerId(pointerIndex);
1745 if (pointerId == mActivePointerId) {
1746 // This was our active pointer going up. Choose a new
1747 // active pointer and adjust accordingly.
1748 // TODO: Make this decision more intelligent.
1749 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1750 mLastMotionY = (int) ev.getY(newPointerIndex);
1751 mActivePointerId = ev.getPointerId(newPointerIndex);
1752 if (mVelocityTracker != null) {
1753 mVelocityTracker.clear();
1754 }
1755 }
1756 }
1757
1758 private void initVelocityTrackerIfNotExists() {
1759 if (mVelocityTracker == null) {
1760 mVelocityTracker = VelocityTracker.obtain();
1761 }
1762 }
1763
1764 private void recycleVelocityTracker() {
1765 if (mVelocityTracker != null) {
1766 mVelocityTracker.recycle();
1767 mVelocityTracker = null;
1768 }
1769 }
1770
1771 private void initOrResetVelocityTracker() {
1772 if (mVelocityTracker == null) {
1773 mVelocityTracker = VelocityTracker.obtain();
1774 } else {
1775 mVelocityTracker.clear();
1776 }
1777 }
1778
Ricky Waicd35def2016-05-03 11:07:07 +01001779 public void setFinishScrollingCallback(Runnable runnable) {
1780 mFinishScrollingCallback = runnable;
1781 }
1782
Selim Cinek9212de82017-02-06 16:04:28 -08001783 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001784 if (mScroller.computeScrollOffset()) {
Selim Cinek67b22602014-03-10 15:40:16 +01001785 int oldY = mOwnScrollY;
Selim Cinek67b22602014-03-10 15:40:16 +01001786 int y = mScroller.getCurrY();
1787
Selim Cinek9212de82017-02-06 16:04:28 -08001788 if (oldY != y) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001789 int range = getScrollRange();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001790 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
1791 float currVelocity = mScroller.getCurrVelocity();
Selim Cinekba819622014-05-13 17:55:53 +02001792 if (currVelocity >= mMinimumVelocity) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001793 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
1794 }
1795 }
Selim Cinek67b22602014-03-10 15:40:16 +01001796
Adrian Roos5153d4a2016-03-22 10:01:56 -07001797 if (mDontClampNextScroll) {
1798 range = Math.max(range, oldY);
1799 }
Selim Cinek9212de82017-02-06 16:04:28 -08001800 customOverScrollBy(y - oldY, oldY, range,
1801 (int) (mMaxOverScroll));
Selim Cinek67b22602014-03-10 15:40:16 +01001802 }
1803
Selim Cinek9212de82017-02-06 16:04:28 -08001804 postOnAnimation(mAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001805 } else {
1806 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001807 if (mFinishScrollingCallback != null) {
1808 mFinishScrollingCallback.run();
1809 }
Selim Cinek67b22602014-03-10 15:40:16 +01001810 }
1811 }
1812
Selim Cinek9212de82017-02-06 16:04:28 -08001813 private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
1814 int maxOverScrollY) {
Selim Cinek4195dd02014-05-19 18:16:14 +02001815
1816 int newScrollY = scrollY + deltaY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001817 final int top = -maxOverScrollY;
1818 final int bottom = maxOverScrollY + scrollRangeY;
1819
1820 boolean clampedY = false;
1821 if (newScrollY > bottom) {
1822 newScrollY = bottom;
1823 clampedY = true;
1824 } else if (newScrollY < top) {
1825 newScrollY = top;
1826 clampedY = true;
1827 }
1828
Selim Cinek9212de82017-02-06 16:04:28 -08001829 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001830
1831 return clampedY;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001832 }
1833
1834 /**
1835 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1836 * overscroll effect based on numPixels. By default this will also cancel animations on the
1837 * same overScroll edge.
1838 *
1839 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1840 * the rubber-banding logic.
1841 * @param onTop Should the effect be applied on top of the scroller.
1842 * @param animate Should an animation be performed.
1843 */
1844 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001845 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001846 }
1847
1848 /**
1849 * Set the effective overScroll amount which will be directly reflected in the layout.
1850 * By default this will also cancel animations on the same overScroll edge.
1851 *
1852 * @param amount The amount to overScroll by.
1853 * @param onTop Should the effect be applied on top of the scroller.
1854 * @param animate Should an animation be performed.
1855 */
1856 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1857 setOverScrollAmount(amount, onTop, animate, true);
1858 }
1859
1860 /**
1861 * Set the effective overScroll amount which will be directly reflected in the layout.
1862 *
1863 * @param amount The amount to overScroll by.
1864 * @param onTop Should the effect be applied on top of the scroller.
1865 * @param animate Should an animation be performed.
1866 * @param cancelAnimators Should running animations be cancelled.
1867 */
1868 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1869 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001870 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1871 }
1872
1873 /**
1874 * Set the effective overScroll amount which will be directly reflected in the layout.
1875 *
1876 * @param amount The amount to overScroll by.
1877 * @param onTop Should the effect be applied on top of the scroller.
1878 * @param animate Should an animation be performed.
1879 * @param cancelAnimators Should running animations be cancelled.
1880 * @param isRubberbanded The value which will be passed to
1881 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
1882 */
1883 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1884 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001885 if (cancelAnimators) {
1886 mStateAnimator.cancelOverScrollAnimators(onTop);
1887 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001888 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001889 }
1890
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001891 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1892 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001893 amount = Math.max(0, amount);
1894 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001895 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001896 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001897 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001898 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001899 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001900 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001901 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001902 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001903 }
1904 }
1905
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001906 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001907 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1908 if (mDontReportNextOverScroll) {
1909 mDontReportNextOverScroll = false;
1910 return;
1911 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001912 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001913 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001914 }
1915 }
1916
1917 public void setOverscrollTopChangedListener(
1918 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1919 mOverscrollTopChangedListener = overscrollTopChangedListener;
1920 }
1921
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001922 public float getCurrentOverScrollAmount(boolean top) {
1923 return mAmbientState.getOverScrollAmount(top);
1924 }
1925
1926 public float getCurrentOverScrolledPixels(boolean top) {
1927 return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
1928 }
1929
1930 private void setOverScrolledPixels(float amount, boolean onTop) {
1931 if (onTop) {
1932 mOverScrolledTopPixels = amount;
1933 } else {
1934 mOverScrolledBottomPixels = amount;
1935 }
1936 }
1937
Selim Cinek9212de82017-02-06 16:04:28 -08001938 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Selim Cinek67b22602014-03-10 15:40:16 +01001939 // Treat animating scrolls differently; see #computeScroll() for why.
1940 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07001941 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001942 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001943 springBack();
1944 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02001945 float overScrollTop = getCurrentOverScrollAmount(true);
1946 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001947 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001948 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001949 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02001950 }
Selim Cinek67b22602014-03-10 15:40:16 +01001951 }
Selim Cinek67b22602014-03-10 15:40:16 +01001952 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08001953 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01001954 }
1955 }
1956
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001957 private void springBack() {
1958 int scrollRange = getScrollRange();
1959 boolean overScrolledTop = mOwnScrollY <= 0;
1960 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
1961 if (overScrolledTop || overScrolledBottom) {
1962 boolean onTop;
1963 float newAmount;
1964 if (overScrolledTop) {
1965 onTop = true;
1966 newAmount = -mOwnScrollY;
Selim Cinekef406062016-09-29 17:33:13 -07001967 setOwnScrollY(0);
Selim Cinek1408eb52014-06-02 14:45:38 +02001968 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001969 } else {
1970 onTop = false;
1971 newAmount = mOwnScrollY - scrollRange;
Selim Cinekef406062016-09-29 17:33:13 -07001972 setOwnScrollY(scrollRange);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001973 }
1974 setOverScrollAmount(newAmount, onTop, false);
1975 setOverScrollAmount(0.0f, onTop, true);
1976 mScroller.forceFinished(true);
1977 }
1978 }
1979
Selim Cinek67b22602014-03-10 15:40:16 +01001980 private int getScrollRange() {
Selim Cineka424c502016-04-05 13:07:54 -07001981 int contentHeight = getContentHeight();
Selim Cinekdb167372016-11-17 15:41:17 -08001982 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07001983 int imeInset = getImeInset();
1984 scrollRange += Math.min(imeInset, Math.max(0,
1985 getContentHeight() - (getHeight() - imeInset)));
1986 return scrollRange;
1987 }
1988
1989 private int getImeInset() {
1990 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01001991 }
1992
Selim Cinek343e6e22014-04-11 21:23:30 +02001993 /**
1994 * @return the first child which has visibility unequal to GONE
1995 */
Selim Cinekb55386d2015-12-16 17:26:49 -08001996 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02001997 int childCount = getChildCount();
1998 for (int i = 0; i < childCount; i++) {
1999 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002000 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002001 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002002 }
2003 }
2004 return null;
2005 }
2006
Selim Cinek4a1ac842014-05-01 15:51:58 +02002007 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002008 * @return the child before the given view which has visibility unequal to GONE
2009 */
2010 public ExpandableView getViewBeforeView(ExpandableView view) {
2011 ExpandableView previousView = null;
2012 int childCount = getChildCount();
2013 for (int i = 0; i < childCount; i++) {
2014 View child = getChildAt(i);
2015 if (child == view) {
2016 return previousView;
2017 }
2018 if (child.getVisibility() != View.GONE) {
2019 previousView = (ExpandableView) child;
2020 }
2021 }
2022 return null;
2023 }
2024
2025 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002026 * @return The first child which has visibility unequal to GONE which is currently below the
2027 * given translationY or equal to it.
2028 */
Selim Cinekef8c2252017-02-10 14:52:18 -08002029 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002030 int childCount = getChildCount();
2031 for (int i = 0; i < childCount; i++) {
2032 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002033 if (child.getVisibility() == View.GONE) {
2034 continue;
2035 }
2036 float rowTranslation = child.getTranslationY();
2037 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002038 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002039 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2040 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2041 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2042 List<ExpandableNotificationRow> notificationChildren =
2043 row.getNotificationChildren();
2044 for (int childIndex = 0; childIndex < notificationChildren.size();
2045 childIndex++) {
2046 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2047 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2048 return rowChild;
2049 }
2050 }
2051 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002052 }
2053 }
2054 return null;
2055 }
2056
2057 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002058 * @return the last child which has visibility unequal to GONE
2059 */
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002060 public View getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002061 int childCount = getChildCount();
2062 for (int i = childCount - 1; i >= 0; i--) {
2063 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002064 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002065 return child;
2066 }
2067 }
2068 return null;
2069 }
2070
Jorim Jaggi069cd032014-05-15 03:09:01 +02002071 /**
2072 * @return the number of children which have visibility unequal to GONE
2073 */
2074 public int getNotGoneChildCount() {
2075 int childCount = getChildCount();
2076 int count = 0;
2077 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002078 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002079 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002080 count++;
2081 }
2082 }
2083 return count;
2084 }
2085
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002086 public int getContentHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01002087 return mContentHeight;
2088 }
2089
2090 private void updateContentHeight() {
2091 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002092 float previousPaddingRequest = mPaddingBetweenElements;
2093 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002094 int numShownItems = 0;
2095 boolean finish = false;
Adrian Roos7a9551a2017-01-11 12:27:49 -08002096 int maxDisplayedNotifications = mAmbientState.isDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002097 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08002098 : mMaxDisplayedNotifications;
2099
Selim Cinek67b22602014-03-10 15:40:16 +01002100 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002101 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Selim Cinek281c2022016-10-13 19:14:43 -07002102 if (expandableView.getVisibility() != View.GONE
2103 && !expandableView.hasNoContentHeight()) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002104 boolean limitReached = maxDisplayedNotifications != -1
2105 && numShownItems >= maxDisplayedNotifications;
Selim Cinekbe2c4432017-05-30 12:11:09 -07002106 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isDark()
2107 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07002108 && expandableView instanceof ExpandableNotificationRow
2109 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
2110 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002111 expandableView = mShelf;
2112 finish = true;
2113 }
Selim Cinek42357e02016-02-24 18:48:01 -08002114 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002115 float padding;
2116 if (increasedPaddingAmount >= 0.0f) {
2117 padding = (int) NotificationUtils.interpolate(
2118 previousPaddingRequest,
2119 mIncreasedPaddingBetweenElements,
2120 increasedPaddingAmount);
2121 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002122 mPaddingBetweenElements,
2123 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002124 increasedPaddingAmount);
2125 } else {
2126 int ownPadding = (int) NotificationUtils.interpolate(
2127 0,
2128 mPaddingBetweenElements,
2129 1.0f + increasedPaddingAmount);
2130 if (previousPaddingAmount > 0.0f) {
2131 padding = (int) NotificationUtils.interpolate(
2132 ownPadding,
2133 mIncreasedPaddingBetweenElements,
2134 previousPaddingAmount);
2135 } else {
2136 padding = ownPadding;
2137 }
2138 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002139 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002140 if (height != 0) {
2141 height += padding;
2142 }
2143 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002144 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002145 numShownItems++;
2146 if (finish) {
2147 break;
2148 }
Selim Cinek67b22602014-03-10 15:40:16 +01002149 }
2150 }
Anthony Chen9fe1ee72017-04-07 13:53:37 -07002151 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002152 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002153 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002154 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002155 }
2156
Adrian Roos7d062c42017-03-30 15:11:43 -07002157 private boolean isPulsing(NotificationData.Entry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002158 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002159 }
2160
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002161 @Override
Selim Cinekbe2c4432017-05-30 12:11:09 -07002162 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002163 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002164 }
2165
Selim Cinekc22fff62016-05-20 12:44:30 -07002166 private void updateScrollability() {
2167 boolean scrollable = getScrollRange() > 0;
2168 if (scrollable != mScrollable) {
2169 mScrollable = scrollable;
2170 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002171 updateForwardAndBackwardScrollability();
2172 }
2173 }
2174
2175 private void updateForwardAndBackwardScrollability() {
2176 boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
2177 boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
2178 boolean changed = forwardScrollable != mForwardScrollable
2179 || backwardsScrollable != mBackwardScrollable;
2180 mForwardScrollable = forwardScrollable;
2181 mBackwardScrollable = backwardsScrollable;
2182 if (changed) {
2183 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002184 }
Selim Cinek67b22602014-03-10 15:40:16 +01002185 }
2186
Selim Cinek6811d722016-01-19 17:53:12 -08002187 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002188 // No need to update the background color if it's not being drawn.
2189 if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002190 return;
2191 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002192
Selim Cinek6811d722016-01-19 17:53:12 -08002193 updateBackgroundBounds();
2194 if (!mCurrentBounds.equals(mBackgroundBounds)) {
Selim Cinek54680902016-10-19 16:49:44 -07002195 boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
2196 || areBoundsAnimating();
2197 if (!isExpanded()) {
2198 abortBackgroundAnimators();
2199 animate = false;
2200 }
2201 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002202 startBackgroundAnimation();
2203 } else {
2204 mCurrentBounds.set(mBackgroundBounds);
2205 applyCurrentBackgroundBounds();
2206 }
2207 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002208 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002209 }
Selim Cinek614576e2016-01-20 10:54:09 -08002210 mAnimateNextBackgroundBottom = false;
2211 mAnimateNextBackgroundTop = false;
2212 }
2213
Selim Cinek54680902016-10-19 16:49:44 -07002214 private void abortBackgroundAnimators() {
2215 if (mBottomAnimator != null) {
2216 mBottomAnimator.cancel();
2217 }
2218 if (mTopAnimator != null) {
2219 mTopAnimator.cancel();
2220 }
2221 }
2222
Selim Cinek614576e2016-01-20 10:54:09 -08002223 private boolean areBoundsAnimating() {
2224 return mBottomAnimator != null || mTopAnimator != null;
2225 }
2226
2227 private void startBackgroundAnimation() {
Selim Cinek01e40192016-05-12 14:57:22 -07002228 // left and right are always instantly applied
2229 mCurrentBounds.left = mBackgroundBounds.left;
2230 mCurrentBounds.right = mBackgroundBounds.right;
Selim Cinek614576e2016-01-20 10:54:09 -08002231 startBottomAnimation();
2232 startTopAnimation();
2233 }
2234
2235 private void startTopAnimation() {
2236 int previousEndValue = mEndAnimationRect.top;
2237 int newEndValue = mBackgroundBounds.top;
2238 ObjectAnimator previousAnimator = mTopAnimator;
2239 if (previousAnimator != null && previousEndValue == newEndValue) {
2240 return;
2241 }
2242 if (!mAnimateNextBackgroundTop) {
2243 // just a local update was performed
2244 if (previousAnimator != null) {
2245 // we need to increase all animation keyframes of the previous animator by the
2246 // relative change to the end value
2247 int previousStartValue = mStartAnimationRect.top;
2248 PropertyValuesHolder[] values = previousAnimator.getValues();
2249 values[0].setIntValues(previousStartValue, newEndValue);
2250 mStartAnimationRect.top = previousStartValue;
2251 mEndAnimationRect.top = newEndValue;
2252 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2253 return;
2254 } else {
2255 // no new animation needed, let's just apply the value
2256 setBackgroundTop(newEndValue);
2257 return;
2258 }
2259 }
2260 if (previousAnimator != null) {
2261 previousAnimator.cancel();
2262 }
2263 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
2264 mCurrentBounds.top, newEndValue);
2265 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2266 animator.setInterpolator(interpolator);
2267 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2268 // remove the tag when the animation is finished
2269 animator.addListener(new AnimatorListenerAdapter() {
2270 @Override
2271 public void onAnimationEnd(Animator animation) {
2272 mStartAnimationRect.top = -1;
2273 mEndAnimationRect.top = -1;
2274 mTopAnimator = null;
2275 }
2276 });
2277 animator.start();
2278 mStartAnimationRect.top = mCurrentBounds.top;
2279 mEndAnimationRect.top = newEndValue;
2280 mTopAnimator = animator;
2281 }
2282
2283 private void startBottomAnimation() {
2284 int previousStartValue = mStartAnimationRect.bottom;
2285 int previousEndValue = mEndAnimationRect.bottom;
2286 int newEndValue = mBackgroundBounds.bottom;
2287 ObjectAnimator previousAnimator = mBottomAnimator;
2288 if (previousAnimator != null && previousEndValue == newEndValue) {
2289 return;
2290 }
2291 if (!mAnimateNextBackgroundBottom) {
2292 // just a local update was performed
2293 if (previousAnimator != null) {
2294 // we need to increase all animation keyframes of the previous animator by the
2295 // relative change to the end value
2296 PropertyValuesHolder[] values = previousAnimator.getValues();
2297 values[0].setIntValues(previousStartValue, newEndValue);
2298 mStartAnimationRect.bottom = previousStartValue;
2299 mEndAnimationRect.bottom = newEndValue;
2300 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2301 return;
2302 } else {
2303 // no new animation needed, let's just apply the value
2304 setBackgroundBottom(newEndValue);
2305 return;
2306 }
2307 }
2308 if (previousAnimator != null) {
2309 previousAnimator.cancel();
2310 }
2311 ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
2312 mCurrentBounds.bottom, newEndValue);
2313 Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
2314 animator.setInterpolator(interpolator);
2315 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2316 // remove the tag when the animation is finished
2317 animator.addListener(new AnimatorListenerAdapter() {
2318 @Override
2319 public void onAnimationEnd(Animator animation) {
2320 mStartAnimationRect.bottom = -1;
2321 mEndAnimationRect.bottom = -1;
2322 mBottomAnimator = null;
2323 }
2324 });
2325 animator.start();
2326 mStartAnimationRect.bottom = mCurrentBounds.bottom;
2327 mEndAnimationRect.bottom = newEndValue;
2328 mBottomAnimator = animator;
2329 }
2330
2331 private void setBackgroundTop(int top) {
2332 mCurrentBounds.top = top;
2333 applyCurrentBackgroundBounds();
2334 }
2335
2336 public void setBackgroundBottom(int bottom) {
2337 mCurrentBounds.bottom = bottom;
2338 applyCurrentBackgroundBounds();
2339 }
2340
2341 private void applyCurrentBackgroundBounds() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002342 // If the background of the notification is not being drawn, then there is no need to
2343 // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
2344 if (!mShouldDrawNotificationBackground) {
2345 return;
2346 }
2347
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002348 final boolean awake = mDarkAmount != 0 || mAmbientState.isDark();
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002349 mScrimController.setExcludedBackgroundArea(
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08002350 mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
Selim Cinek9bfba9c2016-08-08 15:20:06 -07002351 : mCurrentBounds);
Selim Cinek614576e2016-01-20 10:54:09 -08002352 invalidate();
Selim Cinek6811d722016-01-19 17:53:12 -08002353 }
2354
2355 /**
2356 * Update the background bounds to the new desired bounds
2357 */
2358 private void updateBackgroundBounds() {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01002359 if (mAmbientState.isPanelFullWidth()) {
2360 mBackgroundBounds.left = 0;
2361 mBackgroundBounds.right = getWidth();
2362 } else {
2363 getLocationInWindow(mTempInt2);
2364 mBackgroundBounds.left = mTempInt2[0];
2365 mBackgroundBounds.right = mTempInt2[0] + getWidth();
2366 }
Selim Cinekb95fd182017-12-21 13:03:32 -08002367 mBackgroundBounds.left += mSidePaddings;
2368 mBackgroundBounds.right -= mSidePaddings;
Selim Cinek614576e2016-01-20 10:54:09 -08002369 if (!mIsExpanded) {
2370 mBackgroundBounds.top = 0;
2371 mBackgroundBounds.bottom = 0;
Selim Cinek1791f502016-10-07 17:38:03 -04002372 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002373 }
2374 ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
Selim Cinek6811d722016-01-19 17:53:12 -08002375 int top = 0;
2376 if (firstView != null) {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002377 // Round Y up to avoid seeing the background during animation
2378 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
Selim Cinek614576e2016-01-20 10:54:09 -08002379 if (mAnimateNextBackgroundTop
2380 || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
2381 || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
Selim Cinek6811d722016-01-19 17:53:12 -08002382 // we're ending up at the same location as we are now, lets just skip the animation
2383 top = finalTranslationY;
2384 } else {
Lucas Dupin4f67be72017-05-29 11:30:29 -07002385 top = (int) Math.ceil(firstView.getTranslationY());
Selim Cinek6811d722016-01-19 17:53:12 -08002386 }
2387 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002388 ActivatableNotificationView lastView =
2389 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2390 ? mShelf
2391 : mLastVisibleBackgroundChild;
2392 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002393 if (lastView != null) {
Selim Cinekeccb5de2016-10-28 15:04:05 -07002394 int finalTranslationY;
2395 if (lastView == mShelf) {
2396 finalTranslationY = (int) mShelf.getTranslationY();
2397 } else {
2398 finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
2399 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002400 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
Selim Cinekc519dcf2016-11-22 13:45:39 -08002401 int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
Selim Cinek6811d722016-01-19 17:53:12 -08002402 finalBottom = Math.min(finalBottom, getHeight());
Selim Cinek614576e2016-01-20 10:54:09 -08002403 if (mAnimateNextBackgroundBottom
2404 || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
2405 || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
Selim Cinek6811d722016-01-19 17:53:12 -08002406 // we're ending up at the same location as we are now, lets just skip the animation
2407 bottom = finalBottom;
2408 } else {
Selim Cinekc519dcf2016-11-22 13:45:39 -08002409 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2410 - lastView.getClipBottomAmount());
Selim Cinek6811d722016-01-19 17:53:12 -08002411 bottom = Math.min(bottom, getHeight());
2412 }
Selim Cinek3776fe02016-02-04 13:32:43 -08002413 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002414 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002415 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002416 }
Selim Cinek355652a2016-12-07 13:32:12 -08002417 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002418 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002419 } else {
2420 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002421 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002422 }
Selim Cinekd381bc32016-08-15 12:40:57 -07002423 mBackgroundBounds.top = top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002424 mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
Selim Cinek6811d722016-01-19 17:53:12 -08002425 }
2426
Selim Cinek614576e2016-01-20 10:54:09 -08002427 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2428 int childCount = getChildCount();
2429 for (int i = 0; i < childCount; i++) {
2430 View child = getChildAt(i);
2431 if (child.getVisibility() != View.GONE
2432 && child instanceof ExpandableNotificationRow) {
2433 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2434 if (row.isPinned()) {
2435 return row;
2436 }
2437 }
2438 }
2439 return null;
2440 }
2441
2442 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002443 int childCount = getChildCount();
2444 for (int i = childCount - 1; i >= 0; i--) {
2445 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002446 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2447 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002448 return (ActivatableNotificationView) child;
2449 }
2450 }
2451 return null;
2452 }
2453
Selim Cinek614576e2016-01-20 10:54:09 -08002454 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002455 int childCount = getChildCount();
2456 for (int i = 0; i < childCount; i++) {
2457 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002458 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2459 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002460 return (ActivatableNotificationView) child;
2461 }
2462 }
2463 return null;
2464 }
2465
Selim Cinek67b22602014-03-10 15:40:16 +01002466 /**
2467 * Fling the scroll view
2468 *
2469 * @param velocityY The initial velocity in the Y direction. Positive
2470 * numbers mean that the finger/cursor is moving down the screen,
2471 * which means we want to scroll towards the top.
2472 */
Muyuan Li26e30ae2016-04-11 17:31:42 -07002473 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002474 if (getChildCount() > 0) {
Selim Cinek4195dd02014-05-19 18:16:14 +02002475 int scrollRange = getScrollRange();
Selim Cinek67b22602014-03-10 15:40:16 +01002476
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002477 float topAmount = getCurrentOverScrollAmount(true);
2478 float bottomAmount = getCurrentOverScrollAmount(false);
2479 if (velocityY < 0 && topAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002480 setOwnScrollY(mOwnScrollY - (int) topAmount);
Selim Cinek1408eb52014-06-02 14:45:38 +02002481 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002482 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002483 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002484 * mOverflingDistance + topAmount;
2485 } else if (velocityY > 0 && bottomAmount > 0) {
Selim Cinekef406062016-09-29 17:33:13 -07002486 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002487 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002488 mMaxOverScroll = Math.abs(velocityY) / 1000f
2489 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
2490 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002491 } else {
2492 // it will be set once we reach the boundary
2493 mMaxOverScroll = 0.0f;
2494 }
Selim Cinek94ab18c2016-02-25 12:35:51 -08002495 int minScrollY = Math.max(0, scrollRange);
2496 if (mExpandedInThisMotion) {
2497 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2498 }
Selim Cinek9212de82017-02-06 16:04:28 -08002499 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2500 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek67b22602014-03-10 15:40:16 +01002501
Selim Cinek9212de82017-02-06 16:04:28 -08002502 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002503 }
2504 }
2505
Selim Cinek1408eb52014-06-02 14:45:38 +02002506 /**
2507 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2508 * overScroll view (i.e QS).
2509 */
2510 private boolean shouldOverScrollFling(int initialVelocity) {
2511 float topOverScroll = getCurrentOverScrollAmount(true);
2512 return mScrolledToTopOnFirstDown
2513 && !mExpandedInThisMotion
2514 && topOverScroll > mMinTopOverScrollToEscape
2515 && initialVelocity > 0;
2516 }
2517
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002518 /**
2519 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2520 * account.
2521 *
2522 * @param qsHeight the top padding imposed by the quick settings panel
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002523 * @param animate whether to animate the change
2524 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2525 * {@code qsHeight} is the final top padding
2526 */
Jason Monk16ac3772016-02-10 15:39:21 -05002527 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002528 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002529 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002530 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002531 if (topPadding + minStackHeight > getHeight()) {
2532 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002533 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002534 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002535 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002536 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002537 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002538 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002539 }
2540
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002541 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002542 if (isHeadsUpTransition()) {
2543 return getTopHeadsUpPinnedHeight();
2544 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002545 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002546 }
2547
Selim Cineka981d082016-08-03 14:45:31 -07002548 public int getFirstChildIntrinsicHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002549 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002550 int firstChildMinHeight = firstChild != null
2551 ? firstChild.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002552 : mEmptyShadeView != null
Selim Cineka981d082016-08-03 14:45:31 -07002553 ? mEmptyShadeView.getIntrinsicHeight()
Selim Cinek90b6a272016-06-03 16:50:52 -07002554 : mCollapsedSize;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002555 if (mOwnScrollY > 0) {
2556 firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
2557 }
Selim Cinek94c2d822016-07-13 18:50:04 -07002558 return firstChildMinHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002559 }
2560
2561 public float getTopPaddingOverflow() {
2562 return mTopPaddingOverflow;
2563 }
2564
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002565 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002566 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002567 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002568 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002569 int shelfHeight = 0;
Anthony Chen9e05d462017-04-07 10:10:21 -07002570 if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002571 shelfHeight = mShelf.getIntrinsicHeight();
2572 }
2573 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002574 }
2575
Selim Cinek1408eb52014-06-02 14:45:38 +02002576 private int clampPadding(int desiredPadding) {
2577 return Math.max(desiredPadding, mIntrinsicPadding);
2578 }
2579
Selim Cinekfed1ab62014-06-17 14:10:33 -07002580 private float getRubberBandFactor(boolean onTop) {
2581 if (!onTop) {
2582 return RUBBER_BAND_FACTOR_NORMAL;
2583 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002584 if (mExpandedInThisMotion) {
2585 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002586 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002587 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2588 } else if (mScrolledToTopOnFirstDown) {
2589 return 1.0f;
2590 }
2591 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002592 }
2593
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002594 /**
2595 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2596 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2597 * overscroll view (e.g. expand QS).
2598 */
2599 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002600 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002601 || !mScrolledToTopOnFirstDown;
2602 }
2603
Selim Cinek67b22602014-03-10 15:40:16 +01002604 private void endDrag() {
2605 setIsBeingDragged(false);
2606
2607 recycleVelocityTracker();
2608
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002609 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
2610 setOverScrollAmount(0, true /* onTop */, true /* animate */);
2611 }
2612 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
2613 setOverScrollAmount(0, false /* onTop */, true /* animate */);
2614 }
Selim Cinek67b22602014-03-10 15:40:16 +01002615 }
2616
Jorim Jaggi56306252014-07-03 00:40:09 +02002617 private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
2618 ev.offsetLocation(sourceView.getX(), sourceView.getY());
2619 ev.offsetLocation(-targetView.getX(), -targetView.getY());
2620 }
2621
Selim Cinek67b22602014-03-10 15:40:16 +01002622 @Override
2623 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002624 initDownStates(ev);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002625 handleEmptySpaceClick(ev);
Selim Cinek1408eb52014-06-02 14:45:38 +02002626 boolean expandWantsIt = false;
Selim Cinekcb9400a2015-06-03 16:56:13 +02002627 if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002628 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
2629 }
Selim Cinek67b22602014-03-10 15:40:16 +01002630 boolean scrollWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002631 if (!mSwipingInProgress && !mExpandingNotification) {
Selim Cinek67b22602014-03-10 15:40:16 +01002632 scrollWantsIt = onInterceptTouchEventScroll(ev);
2633 }
2634 boolean swipeWantsIt = false;
Selim Cinek1408eb52014-06-02 14:45:38 +02002635 if (!mIsBeingDragged
2636 && !mExpandingNotification
2637 && !mExpandedInThisMotion
Adrian Roosfa139752016-04-27 09:59:08 -07002638 && !mOnlyScrollingInThisMotion
2639 && !mDisallowDismissInThisMotion) {
Selim Cinek67b22602014-03-10 15:40:16 +01002640 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
2641 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07002642 // Check if we need to clear any snooze leavebehinds
2643 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002644 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellorc2dbe492017-03-30 13:22:03 -07002645 if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
2646 && !scrollWantsIt) {
2647 mCheckForLeavebehind = false;
Eliot Courtney47098cb2017-10-18 17:30:30 +09002648 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
2649 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
2650 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07002651 }
2652 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
2653 mCheckForLeavebehind = true;
2654 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002655 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
2656 }
2657
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002658 private void handleEmptySpaceClick(MotionEvent ev) {
2659 switch (ev.getActionMasked()) {
2660 case MotionEvent.ACTION_MOVE:
2661 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
2662 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
2663 mTouchIsClick = false;
2664 }
2665 break;
2666 case MotionEvent.ACTION_UP:
Selim Cinek355652a2016-12-07 13:32:12 -08002667 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002668 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
2669 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
2670 }
2671 break;
2672 }
2673 }
2674
Selim Cinek1408eb52014-06-02 14:45:38 +02002675 private void initDownStates(MotionEvent ev) {
2676 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
2677 mExpandedInThisMotion = false;
2678 mOnlyScrollingInThisMotion = !mScroller.isFinished();
Selim Cinekf7a14c02014-07-07 14:01:46 +02002679 mDisallowScrollingInThisMotion = false;
Adrian Roosfa139752016-04-27 09:59:08 -07002680 mDisallowDismissInThisMotion = false;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01002681 mTouchIsClick = true;
2682 mInitialTouchX = ev.getX();
2683 mInitialTouchY = ev.getY();
Selim Cinek1408eb52014-06-02 14:45:38 +02002684 }
Selim Cinek67b22602014-03-10 15:40:16 +01002685 }
2686
Selim Cinekef5127e2015-12-21 16:55:58 -08002687 public void setChildTransferInProgress(boolean childTransferInProgress) {
2688 mChildTransferInProgress = childTransferInProgress;
2689 }
2690
Christoph Studer068f5922014-04-08 17:43:07 -04002691 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002692 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002693 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002694 // we only call our internal methods if this is actually a removal and not just a
2695 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002696 if (!mChildTransferInProgress) {
Selim Cinekd1395642016-04-28 12:22:42 -07002697 onViewRemovedInternal(child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002698 }
2699 }
2700
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002701 @Override
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002702 public void cleanUpViewState(View child) {
2703 if (child == mTranslatingParentView) {
2704 mTranslatingParentView = null;
2705 }
2706 mCurrentStackScrollState.removeViewStateForView(child);
2707 }
2708
Selim Cinek197823d2016-03-24 13:06:00 -07002709 @Override
2710 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2711 super.requestDisallowInterceptTouchEvent(disallowIntercept);
2712 if (disallowIntercept) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002713 cancelLongPress();
Selim Cinek197823d2016-03-24 13:06:00 -07002714 }
2715 }
2716
Selim Cineka5703182016-05-11 21:23:16 -04002717 private void onViewRemovedInternal(View child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002718 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002719 // This is only a position change, don't do anything special
2720 return;
2721 }
Selim Cinekd1395642016-04-28 12:22:42 -07002722 ExpandableView expandableView = (ExpandableView) child;
2723 expandableView.setOnHeightChangedListener(null);
Christoph Studer068f5922014-04-08 17:43:07 -04002724 mCurrentStackScrollState.removeViewStateForView(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002725 updateScrollStateForRemovedChild(expandableView);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002726 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002727 if (animationGenerated) {
2728 if (!mSwipedOutViews.contains(child)) {
Selim Cineka5703182016-05-11 21:23:16 -04002729 container.getOverlay().add(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002730 } else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002731 container.addTransientView(child, 0);
2732 expandableView.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002733 }
2734 } else {
2735 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002736 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002737 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002738
Selim Cineke9bad242016-06-15 11:46:37 -07002739 focusNextViewIfFocused(child);
2740 }
2741
2742 private void focusNextViewIfFocused(View view) {
2743 if (view instanceof ExpandableNotificationRow) {
2744 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2745 if (row.shouldRefocusOnDismiss()) {
2746 View nextView = row.getChildAfterViewWhenDismissed();
2747 if (nextView == null) {
2748 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2749 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2750 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002751 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002752 }
2753 if (nextView != null) {
2754 nextView.requestAccessibilityFocus();
2755 }
2756 }
2757 }
2758
Selim Cinekc27437b2014-05-14 10:23:33 +02002759 }
2760
Selim Cinekb5605e52015-02-20 18:21:41 +01002761 private boolean isChildInGroup(View child) {
2762 return child instanceof ExpandableNotificationRow
2763 && mGroupManager.isChildInGroupWithSummary(
2764 ((ExpandableNotificationRow) child).getStatusBarNotification());
2765 }
2766
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002767 /**
2768 * Generate a remove animation for a child view.
2769 *
2770 * @param child The view to generate the remove animation for.
2771 * @return Whether an animation was generated.
2772 */
2773 private boolean generateRemoveAnimation(View child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002774 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002775 mAddedHeadsUpChildren.remove(child);
2776 return false;
2777 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002778 if (isClickedHeadsUp(child)) {
2779 // An animation is already running, add it to the Overlay
2780 mClearOverlayViewsWhenFinished.add(child);
2781 return true;
2782 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002783 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002784 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002785 // Generate Animations
2786 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002787 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002788 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002789 } else {
2790 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002791 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002792 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002793 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002794 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002795 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002796 }
2797
Selim Cinek0fccc722015-07-29 17:04:36 -07002798 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002799 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002800 }
2801
Selim Cineke0890e52015-06-17 11:17:08 -07002802 /**
2803 * Remove a removed child view from the heads up animations if it was just added there
2804 *
2805 * @return whether any child was removed from the list to animate
2806 */
2807 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2808 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002809 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2810 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002811 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002812 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002813 mTmpList.add(eventPair);
2814 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002815 }
2816 }
Selim Cineke0890e52015-06-17 11:17:08 -07002817 if (hasAddEvent) {
2818 // This child was just added lets remove all events.
2819 mHeadsUpChangeAnimations.removeAll(mTmpList);
Selim Cinekcafa87f2016-10-26 17:00:17 -07002820 ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002821 }
2822 mTmpList.clear();
2823 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002824 }
2825
Selim Cinek572bbd42014-04-25 16:43:27 +02002826 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01002827 * @param child the child to query
2828 * @return whether a view is not a top level child but a child notification and that group is
2829 * not expanded
2830 */
2831 private boolean isChildInInvisibleGroup(View child) {
2832 if (child instanceof ExpandableNotificationRow) {
2833 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2834 ExpandableNotificationRow groupSummary =
2835 mGroupManager.getGroupSummary(row.getStatusBarNotification());
2836 if (groupSummary != null && groupSummary != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002837 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01002838 }
2839 }
2840 return false;
2841 }
2842
2843 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02002844 * Updates the scroll position when a child was removed
2845 *
2846 * @param removedChild the removed child
2847 */
Selim Cinek61633a82016-01-25 15:54:10 -08002848 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Selim Cinek572bbd42014-04-25 16:43:27 +02002849 int startingPosition = getPositionInLinearLayout(removedChild);
Selim Cineka7ed2c12017-01-23 20:47:24 -08002850 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
2851 int padding;
2852 if (increasedPaddingAmount >= 0) {
2853 padding = (int) NotificationUtils.interpolate(
2854 mPaddingBetweenElements,
2855 mIncreasedPaddingBetweenElements,
2856 increasedPaddingAmount);
2857 } else {
2858 padding = (int) NotificationUtils.interpolate(
2859 0,
2860 mPaddingBetweenElements,
2861 1.0f + increasedPaddingAmount);
2862 }
Selim Cinek61633a82016-01-25 15:54:10 -08002863 int childHeight = getIntrinsicHeight(removedChild) + padding;
Selim Cinek572bbd42014-04-25 16:43:27 +02002864 int endPosition = startingPosition + childHeight;
2865 if (endPosition <= mOwnScrollY) {
2866 // This child is fully scrolled of the top, so we have to deduct its height from the
2867 // scrollPosition
Selim Cinekef406062016-09-29 17:33:13 -07002868 setOwnScrollY(mOwnScrollY - childHeight);
Selim Cinek572bbd42014-04-25 16:43:27 +02002869 } else if (startingPosition < mOwnScrollY) {
2870 // This child is currently being scrolled into, set the scroll position to the start of
2871 // this child
Selim Cinekef406062016-09-29 17:33:13 -07002872 setOwnScrollY(startingPosition);
Selim Cinek572bbd42014-04-25 16:43:27 +02002873 }
2874 }
2875
Selim Cinekd7c4e002014-07-04 18:36:42 +02002876 private int getIntrinsicHeight(View view) {
2877 if (view instanceof ExpandableView) {
2878 ExpandableView expandableView = (ExpandableView) view;
2879 return expandableView.getIntrinsicHeight();
2880 }
2881 return view.getHeight();
2882 }
2883
Selim Cinek1f624952017-06-08 19:11:50 -07002884 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07002885 ExpandableNotificationRow childInGroup = null;
2886 ExpandableNotificationRow requestedRow = null;
2887 if (isChildInGroup(requestedView)) {
2888 // We're asking for a child in a group. Calculate the position of the parent first,
2889 // then within the parent.
2890 childInGroup = (ExpandableNotificationRow) requestedView;
2891 requestedView = requestedRow = childInGroup.getNotificationParent();
2892 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002893 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002894 float previousPaddingRequest = mPaddingBetweenElements;
2895 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02002896 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002897 ExpandableView child = (ExpandableView) getChildAt(i);
2898 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07002899 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08002900 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002901 float padding;
2902 if (increasedPaddingAmount >= 0.0f) {
2903 padding = (int) NotificationUtils.interpolate(
2904 previousPaddingRequest,
2905 mIncreasedPaddingBetweenElements,
2906 increasedPaddingAmount);
2907 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002908 mPaddingBetweenElements,
2909 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002910 increasedPaddingAmount);
2911 } else {
2912 int ownPadding = (int) NotificationUtils.interpolate(
2913 0,
2914 mPaddingBetweenElements,
2915 1.0f + increasedPaddingAmount);
2916 if (previousPaddingAmount > 0.0f) {
2917 padding = (int) NotificationUtils.interpolate(
2918 ownPadding,
2919 mIncreasedPaddingBetweenElements,
2920 previousPaddingAmount);
2921 } else {
2922 padding = ownPadding;
2923 }
2924 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08002925 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002926 if (position != 0) {
2927 position += padding;
2928 }
2929 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002930 }
Adrian Roos4a579672016-05-24 16:54:37 -07002931 if (child == requestedView) {
2932 if (requestedRow != null) {
2933 position += requestedRow.getPositionOfChild(childInGroup);
2934 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002935 return position;
2936 }
Selim Cinek61633a82016-01-25 15:54:10 -08002937 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02002938 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002939 }
2940 }
2941 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02002942 }
2943
2944 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002945 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02002946 super.onViewAdded(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002947 onViewAddedInternal(child);
2948 }
2949
Selim Cinek614576e2016-01-20 10:54:09 -08002950 private void updateFirstAndLastBackgroundViews() {
2951 ActivatableNotificationView firstChild = getFirstChildWithBackground();
2952 ActivatableNotificationView lastChild = getLastChildWithBackground();
2953 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek29aab962018-02-27 17:05:45 -08002954 mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
2955 mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
Selim Cinek614576e2016-01-20 10:54:09 -08002956 } else {
2957 mAnimateNextBackgroundTop = false;
2958 mAnimateNextBackgroundBottom = false;
2959 }
2960 mFirstVisibleBackgroundChild = firstChild;
2961 mLastVisibleBackgroundChild = lastChild;
Selim Cinekdb167372016-11-17 15:41:17 -08002962 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Selim Cinek29aab962018-02-27 17:05:45 -08002963 mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
2964 mLastVisibleBackgroundChild);
Selim Cinek515b2032017-11-15 10:20:19 -08002965 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002966 }
2967
Selim Cinekb5605e52015-02-20 18:21:41 +01002968 private void onViewAddedInternal(View child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002969 updateHideSensitiveForChild(child);
Jorim Jaggibe565df2014-04-28 17:51:23 +02002970 ((ExpandableView) child).setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00002971 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02002972 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02002973 updateChronometerForChild(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02002974 }
2975
Selim Cinekd06c41c2015-07-06 14:51:36 -07002976 private void updateHideSensitiveForChild(View child) {
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002977 if (child instanceof ExpandableView) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07002978 ExpandableView expandableView = (ExpandableView) child;
Selim Cinekcfcd23e2016-05-05 12:31:42 -04002979 expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07002980 }
2981 }
2982
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002983 @Override
Selim Cinekd1395642016-04-28 12:22:42 -07002984 public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
2985 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01002986 }
2987
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002988 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01002989 public void notifyGroupChildAdded(View row) {
2990 onViewAddedInternal(row);
2991 }
2992
Jorim Jaggi75c95042014-05-16 19:09:59 +02002993 public void setAnimationsEnabled(boolean animationsEnabled) {
2994 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02002995 updateNotificationAnimationStates();
2996 }
2997
2998 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07002999 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003000 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003001 int childCount = getChildCount();
3002 for (int i = 0; i < childCount; i++) {
3003 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003004 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003005 updateAnimationState(running, child);
3006 }
3007 }
3008
Selim Cinek51ae05d2014-09-09 15:51:38 +02003009 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003010 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003011 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003012 }
3013
Selim Cinek2627d722018-01-19 12:16:49 -08003014 @Override
3015 public void setExpandingNotification(ExpandableNotificationRow row) {
3016 mAmbientState.setExpandingNotification(row);
3017 requestChildrenUpdate();
3018 }
3019
3020 @Override
3021 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3022 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3023 requestChildrenUpdate();
3024 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003025
Selim Cinekcab4a602014-09-03 14:47:57 +02003026 private void updateAnimationState(boolean running, View child) {
3027 if (child instanceof ExpandableNotificationRow) {
3028 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3029 row.setIconAnimationRunning(running);
3030 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003031 }
3032
3033 public boolean isAddOrRemoveAnimationPending() {
3034 return mNeedsAnimation
3035 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3036 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003037
3038 @Override
Jorim Jaggif6411742014-08-05 17:10:43 +00003039 public void generateAddAnimation(View child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003040 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003041 // Generate Animations
3042 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003043 if (fromMoreCard) {
3044 mFromMoreCardAdditions.add(child);
3045 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003046 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003047 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003048 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003049 mAddedHeadsUpChildren.add(child);
3050 mChildrenToAddAnimated.remove(child);
3051 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003052 }
3053
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003054 @Override
Selim Cinek572bbd42014-04-25 16:43:27 +02003055 public void changeViewPosition(View child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003056 int currentIndex = indexOfChild(child);
3057 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003058 mChangePositionInProgress = true;
Adrian Roos14503e22016-03-09 14:01:24 -08003059 ((ExpandableView)child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003060 removeView(child);
3061 addView(child, newIndex);
Adrian Roos14503e22016-03-09 14:01:24 -08003062 ((ExpandableView)child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003063 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003064 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003065 mChildrenChangingPositions.add(child);
3066 mNeedsAnimation = true;
3067 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003068 }
3069 }
3070
Selim Cinekf4c19962014-05-01 21:55:31 +02003071 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003072 if (mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003073 generateChildHierarchyEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003074 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003075 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003076 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003077 setAnimationRunning(true);
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003078 mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
3079 mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003080 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003081 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003082 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003083 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003084 } else {
3085 applyCurrentState();
3086 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003087 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003088 }
3089
3090 private void generateChildHierarchyEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003091 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003092 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003093 generateChildAdditionEvents();
3094 generatePositionChangeEvents();
Selim Cinekeb973562014-05-02 17:07:49 +02003095 generateSnapBackEvents();
3096 generateDragEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003097 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003098 generateActivateEvent();
3099 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003100 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003101 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003102 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003103 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003104 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003105 generateAnimateEverythingEvent();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003106 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003107 }
3108
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003109 private void generateHeadsUpAnimationEvents() {
3110 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003111 ExpandableNotificationRow row = eventPair.first;
3112 boolean isHeadsUp = eventPair.second;
3113 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3114 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003115 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003116 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003117 type = row.wasJustClicked()
3118 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3119 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003120 if (row.isChildInGroup()) {
3121 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003122 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003123 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003124 } else {
Selim Cinekbbcebde2016-11-09 18:28:20 -08003125 ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
Selim Cinekeaee9c02015-06-25 11:04:20 -04003126 if (viewState == null) {
3127 // A view state was never generated for this view, so we don't need to animate
3128 // this. This may happen with notification children.
3129 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003130 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003131 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3132 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3133 // Our custom add animation
3134 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3135 } else {
3136 // Normal add animation
3137 type = AnimationEvent.ANIMATION_TYPE_ADD;
3138 }
3139 onBottom = !pinnedAndClosed;
3140 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003141 }
3142 AnimationEvent event = new AnimationEvent(row, type);
3143 event.headsUpFromBottom = onBottom;
3144 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003145 }
3146 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003147 mAddedHeadsUpChildren.clear();
3148 }
3149
Selim Cinekbbcebde2016-11-09 18:28:20 -08003150 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003151 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3152 return false;
3153 }
3154 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003155 }
3156
Selim Cinekb5605e52015-02-20 18:21:41 +01003157 private void generateGroupExpansionEvent() {
3158 // Generate a group expansion/collapsing event if there is such a group at all
3159 if (mExpandedGroupView != null) {
3160 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3161 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3162 mExpandedGroupView = null;
3163 }
3164 }
3165
Selim Cineka5e211b2014-08-11 17:35:48 +02003166 private void generateViewResizeEvent() {
3167 if (mNeedViewResizeAnimation) {
3168 mAnimationEvents.add(
3169 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3170 }
3171 mNeedViewResizeAnimation = false;
3172 }
3173
Selim Cinekeb973562014-05-02 17:07:49 +02003174 private void generateSnapBackEvents() {
3175 for (View child : mSnappedBackChildren) {
3176 mAnimationEvents.add(new AnimationEvent(child,
3177 AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
3178 }
3179 mSnappedBackChildren.clear();
3180 }
3181
3182 private void generateDragEvents() {
3183 for (View child : mDragAnimPendingChildren) {
3184 mAnimationEvents.add(new AnimationEvent(child,
3185 AnimationEvent.ANIMATION_TYPE_START_DRAG));
3186 }
3187 mDragAnimPendingChildren.clear();
3188 }
3189
Selim Cinek572bbd42014-04-25 16:43:27 +02003190 private void generateChildRemovalEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003191 for (View child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003192 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003193
3194 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003195 float removedTranslation = child.getTranslationY();
3196 boolean ignoreChildren = true;
3197 if (child instanceof ExpandableNotificationRow) {
3198 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3199 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3200 removedTranslation = row.getTranslationWhenRemoved();
3201 ignoreChildren = false;
3202 }
Selim Cinek51052042017-07-04 12:07:55 +02003203 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003204 }
Selim Cinek51052042017-07-04 12:07:55 +02003205 if (!childWasSwipedOut) {
3206 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003207 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Selim Cinek51052042017-07-04 12:07:55 +02003208 }
3209 int animationType = childWasSwipedOut
3210 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3211 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3212 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003213 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3214 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003215 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003216 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003217 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003218 mChildrenToRemoveAnimated.clear();
3219 }
3220
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003221 private void generatePositionChangeEvents() {
3222 for (View child : mChildrenChangingPositions) {
3223 mAnimationEvents.add(new AnimationEvent(child,
3224 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3225 }
3226 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003227 if (mGenerateChildOrderChangedEvent) {
3228 mAnimationEvents.add(new AnimationEvent(null,
3229 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3230 mGenerateChildOrderChangedEvent = false;
3231 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003232 }
3233
Selim Cinek572bbd42014-04-25 16:43:27 +02003234 private void generateChildAdditionEvents() {
Selim Cinekeb973562014-05-02 17:07:49 +02003235 for (View child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003236 if (mFromMoreCardAdditions.contains(child)) {
3237 mAnimationEvents.add(new AnimationEvent(child,
3238 AnimationEvent.ANIMATION_TYPE_ADD,
3239 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3240 } else {
3241 mAnimationEvents.add(new AnimationEvent(child,
3242 AnimationEvent.ANIMATION_TYPE_ADD));
3243 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003244 }
3245 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003246 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003247 }
3248
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003249 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003250 if (mTopPaddingNeedsAnimation) {
3251 mAnimationEvents.add(
3252 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
3253 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003254 mTopPaddingNeedsAnimation = false;
3255 }
3256
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003257 private void generateActivateEvent() {
3258 if (mActivateNeedsAnimation) {
3259 mAnimationEvents.add(
3260 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3261 }
3262 mActivateNeedsAnimation = false;
3263 }
3264
Selim Cinekd9acca52014-09-01 22:33:25 +02003265 private void generateAnimateEverythingEvent() {
3266 if (mEverythingNeedsAnimation) {
3267 mAnimationEvents.add(
3268 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3269 }
3270 mEverythingNeedsAnimation = false;
3271 }
3272
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003273 private void generateDimmedEvent() {
3274 if (mDimmedNeedsAnimation) {
3275 mAnimationEvents.add(
3276 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3277 }
3278 mDimmedNeedsAnimation = false;
3279 }
3280
Jorim Jaggiae441282014-08-01 02:45:18 +02003281 private void generateHideSensitiveEvent() {
3282 if (mHideSensitiveNeedsAnimation) {
3283 mAnimationEvents.add(
3284 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3285 }
3286 mHideSensitiveNeedsAnimation = false;
3287 }
3288
John Spurlockbf370992014-06-17 13:58:31 -04003289 private void generateDarkEvent() {
3290 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003291 AnimationEvent ev = new AnimationEvent(null,
3292 AnimationEvent.ANIMATION_TYPE_DARK,
3293 new AnimationFilter()
3294 .animateDark()
3295 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003296 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3297 mAnimationEvents.add(ev);
Selim Cinek972123d2016-05-03 14:25:58 -07003298 startBackgroundFadeIn();
John Spurlockbf370992014-06-17 13:58:31 -04003299 }
3300 mDarkNeedsAnimation = false;
3301 }
3302
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003303 private void generateGoToFullShadeEvent() {
3304 if (mGoToFullShadeNeedsAnimation) {
3305 mAnimationEvents.add(
3306 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3307 }
3308 mGoToFullShadeNeedsAnimation = false;
3309 }
3310
Selim Cinek67b22602014-03-10 15:40:16 +01003311 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02003312 if (!isScrollingEnabled()) {
3313 return false;
3314 }
Selim Cinek67b22602014-03-10 15:40:16 +01003315 /*
3316 * This method JUST determines whether we want to intercept the motion.
3317 * If we return true, onMotionEvent will be called and we do the actual
3318 * scrolling there.
3319 */
3320
3321 /*
3322 * Shortcut the most recurring case: the user is in the dragging
Chris Wren5d53df42015-06-26 11:26:03 -04003323 * state and is moving their finger. We want to intercept this
Selim Cinek67b22602014-03-10 15:40:16 +01003324 * motion.
3325 */
3326 final int action = ev.getAction();
3327 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
3328 return true;
3329 }
3330
Selim Cinek67b22602014-03-10 15:40:16 +01003331 switch (action & MotionEvent.ACTION_MASK) {
3332 case MotionEvent.ACTION_MOVE: {
3333 /*
3334 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04003335 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01003336 */
3337
3338 /*
3339 * Locally do absolute value. mLastMotionY is set to the y value
3340 * of the down event.
3341 */
3342 final int activePointerId = mActivePointerId;
3343 if (activePointerId == INVALID_POINTER) {
3344 // If we don't have a valid id, the touch down wasn't on content.
3345 break;
3346 }
3347
3348 final int pointerIndex = ev.findPointerIndex(activePointerId);
3349 if (pointerIndex == -1) {
3350 Log.e(TAG, "Invalid pointerId=" + activePointerId
3351 + " in onInterceptTouchEvent");
3352 break;
3353 }
3354
3355 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02003356 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01003357 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02003358 final int xDiff = Math.abs(x - mDownX);
3359 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01003360 setIsBeingDragged(true);
3361 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003362 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01003363 initVelocityTrackerIfNotExists();
3364 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01003365 }
3366 break;
3367 }
3368
3369 case MotionEvent.ACTION_DOWN: {
3370 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05303371 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07003372 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01003373 setIsBeingDragged(false);
3374 recycleVelocityTracker();
3375 break;
3376 }
3377
3378 /*
3379 * Remember location of down touch.
3380 * ACTION_DOWN always refers to pointer index 0.
3381 */
3382 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02003383 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01003384 mActivePointerId = ev.getPointerId(0);
3385
3386 initOrResetVelocityTracker();
3387 mVelocityTracker.addMovement(ev);
3388 /*
3389 * If being flinged and user touches the screen, initiate drag;
3390 * otherwise don't. mScroller.isFinished should be false when
3391 * being flinged.
3392 */
3393 boolean isBeingDragged = !mScroller.isFinished();
3394 setIsBeingDragged(isBeingDragged);
3395 break;
3396 }
3397
3398 case MotionEvent.ACTION_CANCEL:
3399 case MotionEvent.ACTION_UP:
3400 /* Release the drag */
3401 setIsBeingDragged(false);
3402 mActivePointerId = INVALID_POINTER;
3403 recycleVelocityTracker();
3404 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
Selim Cinek9212de82017-02-06 16:04:28 -08003405 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01003406 }
3407 break;
3408 case MotionEvent.ACTION_POINTER_UP:
3409 onSecondaryPointerUp(ev);
3410 break;
3411 }
3412
3413 /*
3414 * The only time we want to intercept motion events is if we are in the
3415 * drag mode.
3416 */
3417 return mIsBeingDragged;
3418 }
3419
Muyuan Li333a4fc2016-04-16 17:13:46 -07003420 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
3421 return new StackScrollAlgorithm(context);
3422 }
3423
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003424 /**
3425 * @return Whether the specified motion event is actually happening over the content.
3426 */
3427 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01003428 return isInContentBounds(event.getY());
3429 }
3430
3431 /**
3432 * @return Whether a y coordinate is inside the content.
3433 */
3434 public boolean isInContentBounds(float y) {
3435 return y < getHeight() - getEmptyBottomMargin();
Jorim Jaggife6bfa62014-05-07 23:23:18 +02003436 }
3437
Selim Cinek67b22602014-03-10 15:40:16 +01003438 private void setIsBeingDragged(boolean isDragged) {
3439 mIsBeingDragged = isDragged;
3440 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003441 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003442 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003443 }
3444 }
3445
3446 @Override
3447 public void onWindowFocusChanged(boolean hasWindowFocus) {
3448 super.onWindowFocusChanged(hasWindowFocus);
3449 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003450 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01003451 }
3452 }
Selim Cinekfab078b2014-03-27 22:45:58 +01003453
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003454 @Override
Adrian Roos181385c2016-05-05 17:45:44 -04003455 public void clearChildFocus(View child) {
3456 super.clearChildFocus(child);
3457 if (mForcedScroll == child) {
3458 mForcedScroll = null;
3459 }
3460 }
3461
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003462 public void requestDisallowLongPress() {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003463 cancelLongPress();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07003464 }
3465
Adrian Roosfa139752016-04-27 09:59:08 -07003466 public void requestDisallowDismiss() {
3467 mDisallowDismissInThisMotion = true;
3468 }
3469
Geoffrey Pitsch409db272017-08-28 14:51:52 +00003470 public void cancelLongPress() {
3471 mSwipeHelper.cancelLongPress();
Selim Cinek1408eb52014-06-02 14:45:38 +02003472 }
3473
Selim Cinekfab078b2014-03-27 22:45:58 +01003474 @Override
3475 public boolean isScrolledToTop() {
3476 return mOwnScrollY == 0;
3477 }
3478
3479 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003480 public boolean isScrolledToBottom() {
3481 return mOwnScrollY >= getScrollRange();
3482 }
3483
3484 @Override
Selim Cinekfab078b2014-03-27 22:45:58 +01003485 public View getHostView() {
3486 return this;
3487 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003488
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003489 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08003490 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01003491 }
3492
Mady Mellorc2dbe492017-03-30 13:22:03 -07003493 public void checkSnoozeLeavebehind() {
3494 if (mCheckForLeavebehind) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09003495 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
3496 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3497 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003498 mCheckForLeavebehind = false;
3499 }
3500 }
3501
3502 public void resetCheckSnoozeLeavebehind() {
3503 mCheckForLeavebehind = true;
3504 }
3505
Selim Cinek1685e632014-04-08 02:27:49 +02003506 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003507 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003508 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07003509 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02003510 }
3511
3512 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02003513 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07003514 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08003515 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02003516 if (!mIsExpanded) {
Selim Cinekef406062016-09-29 17:33:13 -07003517 setOwnScrollY(0);
Jason Monk2a6ea9c2017-01-26 11:14:51 -05003518 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003519 clearTemporaryViews();
3520 clearUserLockedViews();
3521 }
3522 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01003523
Selim Cinek5b1591a2017-07-03 17:05:01 +02003524 private void clearUserLockedViews() {
3525 for (int i = 0; i < getChildCount(); i++) {
3526 ExpandableView child = (ExpandableView) getChildAt(i);
3527 if (child instanceof ExpandableNotificationRow) {
3528 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3529 row.setUserLocked(false);
3530 }
3531 }
3532 }
3533
3534 private void clearTemporaryViews() {
3535 // lets make sure nothing is in the overlay / transient anymore
3536 clearTemporaryViews(this);
3537 for (int i = 0; i < getChildCount(); i++) {
3538 ExpandableView child = (ExpandableView) getChildAt(i);
3539 if (child instanceof ExpandableNotificationRow) {
3540 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3541 clearTemporaryViews(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07003542 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02003543 }
Selim Cinek1685e632014-04-08 02:27:49 +02003544 }
3545
Selim Cineka5703182016-05-11 21:23:16 -04003546 private void clearTemporaryViews(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07003547 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04003548 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07003549 }
Selim Cineka5703182016-05-11 21:23:16 -04003550 if (viewGroup != null) {
3551 viewGroup.getOverlay().clear();
3552 }
Selim Cinekd1395642016-04-28 12:22:42 -07003553 }
3554
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003555 public void onPanelTrackingStarted() {
3556 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003557 mAmbientState.setPanelTracking(true);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003558 }
3559 public void onPanelTrackingStopped() {
3560 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08003561 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07003562 }
3563
Selim Cinekb24e0a92015-06-09 20:17:30 -07003564 public void resetScrollPosition() {
3565 mScroller.abortAnimation();
Selim Cinekef406062016-09-29 17:33:13 -07003566 setOwnScrollY(0);
Selim Cinekb24e0a92015-06-09 20:17:30 -07003567 }
3568
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02003569 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02003570 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02003571 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02003572 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003573 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08003574 if (!mIsExpanded) {
3575 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02003576 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08003577 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003578 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02003579 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01003580 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02003581 }
3582 }
3583
3584 private void updateChronometers() {
3585 int childCount = getChildCount();
3586 for (int i = 0; i < childCount; i++) {
3587 updateChronometerForChild(getChildAt(i));
3588 }
3589 }
3590
3591 private void updateChronometerForChild(View child) {
3592 if (child instanceof ExpandableNotificationRow) {
3593 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3594 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02003595 }
Selim Cinek1685e632014-04-08 02:27:49 +02003596 }
3597
Jorim Jaggibe565df2014-04-28 17:51:23 +02003598 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01003599 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003600 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02003601 updateScrollPositionOnExpandInBottom(view);
3602 clampScrollPosition();
Selim Cinekaef92ef2014-06-06 18:06:04 +02003603 notifyHeightChangeListener(view);
Selim Cinekbc243a92016-09-27 16:35:13 -07003604 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
3605 ? (ExpandableNotificationRow) view
3606 : null;
3607 if (row != null && (row == mFirstVisibleBackgroundChild
3608 || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
3609 updateAlgorithmLayoutMinHeight();
3610 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003611 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08003612 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01003613 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003614 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02003615 }
3616
Selim Cineka5e211b2014-08-11 17:35:48 +02003617 @Override
3618 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02003619 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02003620 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02003621 }
3622
Selim Cinekf7a14c02014-07-07 14:01:46 +02003623 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07003624 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02003625 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcb9400a2015-06-03 16:56:13 +02003626 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003627 if (row.isSummaryWithChildren()) {
3628 return;
3629 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02003630 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08003631 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07003632 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08003633 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07003634 }
Selim Cinekdb167372016-11-17 15:41:17 -08003635 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Anthony Chen9e05d462017-04-07 10:10:21 -07003636 if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08003637 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
3638 }
3639 if (endPosition > layoutEnd) {
3640 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
Selim Cinekf7a14c02014-07-07 14:01:46 +02003641 mDisallowScrollingInThisMotion = true;
3642 }
3643 }
3644 }
3645 }
3646
Jorim Jaggibe565df2014-04-28 17:51:23 +02003647 public void setOnHeightChangedListener(
3648 ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
3649 this.mOnHeightChangedListener = mOnHeightChangedListener;
3650 }
3651
Selim Cinek3a9c10a2014-10-28 14:21:10 +01003652 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
3653 mOnEmptySpaceClickListener = listener;
3654 }
3655
Selim Cinek572bbd42014-04-25 16:43:27 +02003656 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08003657 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02003658 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003659 runAnimationFinishedRunnables();
Selim Cinek0fccc722015-07-29 17:04:36 -07003660 clearViewOverlays();
Selim Cinek8fc78752016-07-13 14:34:56 -07003661 clearHeadsUpDisappearRunning();
3662 }
3663
3664 private void clearHeadsUpDisappearRunning() {
3665 for (int i = 0; i < getChildCount(); i++) {
3666 View view = getChildAt(i);
3667 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07003668 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07003669 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003670 if (row.isSummaryWithChildren()) {
3671 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07003672 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07003673 }
3674 }
Selim Cinek8fc78752016-07-13 14:34:56 -07003675 }
3676 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003677 }
3678
3679 private void clearViewOverlays() {
3680 for (View view : mClearOverlayViewsWhenFinished) {
Selim Cineka5703182016-05-11 21:23:16 -04003681 StackStateAnimator.removeFromOverlay(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07003682 }
Selim Cinekc8df0642017-02-23 17:01:04 -08003683 mClearOverlayViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07003684 }
3685
3686 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003687 for (Runnable runnable : mAnimationFinishedRunnables) {
3688 runnable.run();
3689 }
3690 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003691 }
3692
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003693 /**
3694 * See {@link AmbientState#setDimmed}.
3695 */
3696 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07003697 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003698 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003699 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003700 mDimmedNeedsAnimation = true;
3701 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08003702 animateDimmed(dimmed);
3703 } else {
3704 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003705 }
3706 requestChildrenUpdate();
3707 }
3708
Selim Cinek8a9308d2017-08-24 09:31:08 -07003709 @VisibleForTesting
3710 boolean isDimmed() {
3711 return mAmbientState.isDimmed();
3712 }
3713
Selim Cinekd35c2792016-01-21 13:20:57 -08003714 private void setDimAmount(float dimAmount) {
3715 mDimAmount = dimAmount;
3716 updateBackgroundDimming();
3717 }
3718
3719 private void animateDimmed(boolean dimmed) {
3720 if (mDimAnimator != null) {
3721 mDimAnimator.cancel();
3722 }
3723 float target = dimmed ? 1.0f : 0.0f;
3724 if (target == mDimAmount) {
3725 return;
3726 }
3727 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
3728 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
3729 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3730 mDimAnimator.addListener(mDimEndListener);
3731 mDimAnimator.addUpdateListener(mDimUpdateListener);
3732 mDimAnimator.start();
3733 }
3734
Jorim Jaggiae441282014-08-01 02:45:18 +02003735 public void setHideSensitive(boolean hideSensitive, boolean animate) {
3736 if (hideSensitive != mAmbientState.isHideSensitive()) {
3737 int childCount = getChildCount();
3738 for (int i = 0; i < childCount; i++) {
3739 ExpandableView v = (ExpandableView) getChildAt(i);
3740 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
3741 }
3742 mAmbientState.setHideSensitive(hideSensitive);
3743 if (animate && mAnimationsEnabled) {
3744 mHideSensitiveNeedsAnimation = true;
3745 mNeedsAnimation = true;
3746 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08003747 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02003748 requestChildrenUpdate();
3749 }
3750 }
3751
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003752 /**
3753 * See {@link AmbientState#setActivatedChild}.
3754 */
Selim Cineka32ab602014-06-11 15:06:01 +02003755 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003756 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02003757 if (mAnimationsEnabled) {
3758 mActivateNeedsAnimation = true;
3759 mNeedsAnimation = true;
3760 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003761 requestChildrenUpdate();
3762 }
3763
Selim Cineka32ab602014-06-11 15:06:01 +02003764 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003765 return mAmbientState.getActivatedChild();
3766 }
3767
Selim Cinek572bbd42014-04-25 16:43:27 +02003768 private void applyCurrentState() {
Selim Cinek572bbd42014-04-25 16:43:27 +02003769 mCurrentStackScrollState.apply();
Selim Cinekf4c19962014-05-01 21:55:31 +02003770 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003771 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02003772 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07003773 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07003774 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08003775 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003776 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003777 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08003778 }
3779
3780 private void updateViewShadows() {
3781 // we need to work around an issue where the shadow would not cast between siblings when
3782 // their z difference is between 0 and 0.1
3783
3784 // Lefts first sort by Z difference
3785 for (int i = 0; i < getChildCount(); i++) {
3786 ExpandableView child = (ExpandableView) getChildAt(i);
3787 if (child.getVisibility() != GONE) {
3788 mTmpSortedChildren.add(child);
3789 }
3790 }
3791 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
3792
3793 // Now lets update the shadow for the views
3794 ExpandableView previous = null;
3795 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
3796 ExpandableView expandableView = mTmpSortedChildren.get(i);
3797 float translationZ = expandableView.getTranslationZ();
3798 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
3799 float diff = otherZ - translationZ;
3800 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
3801 // There is no fake shadow to be drawn
3802 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
3803 } else {
3804 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07003805 expandableView.getTranslationY() - previous.getExtraBottomPadding();
3806 expandableView.setFakeShadowIntensity(
3807 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08003808 previous.getOutlineAlpha(), (int) yLocation,
3809 previous.getOutlineTranslation());
3810 }
3811 previous = expandableView;
3812 }
3813
3814 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02003815 }
3816
Lucas Dupine17ce522017-07-17 15:45:06 -07003817 /**
3818 * Update colors of "dismiss" and "empty shade" views.
3819 *
3820 * @param lightTheme True if light theme should be used.
3821 */
3822 public void updateDecorViews(boolean lightTheme) {
3823 if (lightTheme == mUsingLightTheme) {
3824 return;
3825 }
3826 mUsingLightTheme = lightTheme;
3827 Context context = new ContextThemeWrapper(mContext,
3828 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
3829 final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
3830 mDismissView.setTextColor(textColor);
3831 mEmptyShadeView.setTextColor(textColor);
3832 }
3833
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003834 public void goToFullShade(long delay) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07003835 if (mDismissView != null) {
3836 mDismissView.setInvisible();
3837 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003838 mEmptyShadeView.setInvisible();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003839 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003840 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003841 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003842 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02003843 }
3844
Selim Cinek1408eb52014-06-02 14:45:38 +02003845 public void cancelExpandHelper() {
3846 mExpandHelper.cancel();
3847 }
3848
3849 public void setIntrinsicPadding(int intrinsicPadding) {
3850 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07003851 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02003852 }
3853
Jorim Jaggi30c305c2014-07-01 23:34:41 +02003854 public int getIntrinsicPadding() {
3855 return mIntrinsicPadding;
3856 }
3857
Christoph Studer6e3eceb2014-04-01 18:40:27 +02003858 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02003859 * @return the y position of the first notification
3860 */
3861 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07003862 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02003863 }
3864
Selim Cinekc0ce82d2014-06-10 13:21:15 +02003865 @Override
3866 public boolean shouldDelayChildPressedState() {
3867 return true;
3868 }
3869
Jorim Jaggi457cc352014-06-02 22:47:42 +02003870 /**
John Spurlockbf370992014-06-17 13:58:31 -04003871 * See {@link AmbientState#setDark}.
3872 */
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003873 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02003874 if (mAmbientState.isDark() == dark) {
3875 return;
3876 }
John Spurlockbf370992014-06-17 13:58:31 -04003877 mAmbientState.setDark(dark);
3878 if (animate && mAnimationsEnabled) {
3879 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003880 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
John Spurlockbf370992014-06-17 13:58:31 -04003881 mNeedsAnimation = true;
Selim Cinek6811d722016-01-19 17:53:12 -08003882 } else {
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08003883 setDarkAmount(dark ? 1f : 0f);
Selim Cinek6811d722016-01-19 17:53:12 -08003884 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08003885 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08003886 requestChildrenUpdate();
3887 applyCurrentBackgroundBounds();
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003888 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003889 updateContentHeight();
Lucas Dupin0cd882f2018-01-30 12:19:49 -08003890 updateAntiBurnInTranslation();
Adrian Roos7a9551a2017-01-11 12:27:49 -08003891 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04003892 }
3893
Lucas Dupin0cd882f2018-01-30 12:19:49 -08003894 private void updateAntiBurnInTranslation() {
3895 setTranslationX(mAmbientState.isDark() ? mAntiBurnInOffsetX : 0);
3896 }
3897
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003898 /**
3899 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
3900 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
3901 * {@link #mAmbientState}'s dark mode is toggled.
3902 */
3903 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08003904 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07003905 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08003906 }
3907
Lucas Dupind285cf02018-01-18 09:18:23 -08003908 private void setDarkAmount(float darkAmount) {
3909 mDarkAmount = darkAmount;
Selim Cinek972123d2016-05-03 14:25:58 -07003910 updateBackgroundDimming();
3911 }
3912
Lucas Dupind285cf02018-01-18 09:18:23 -08003913 public float getDarkAmount() {
3914 return mDarkAmount;
Selim Cinek972123d2016-05-03 14:25:58 -07003915 }
3916
3917 private void startBackgroundFadeIn() {
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08003918 ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount, 0f);
Adrian Roos28f90c72017-05-08 17:24:26 -07003919 fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
Selim Cinek972123d2016-05-03 14:25:58 -07003920 fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
3921 fadeAnimator.start();
3922 }
3923
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003924 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07003925 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003926 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3927 }
3928 if (screenLocation.y > getBottomMostNotificationBottom()) {
3929 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
3930 }
3931 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
3932 if (child != null) {
3933 return getNotGoneIndex(child);
3934 } else {
3935 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
3936 }
3937 }
3938
3939 private int getNotGoneIndex(View child) {
3940 int count = getChildCount();
3941 int notGoneIndex = 0;
3942 for (int i = 0; i < count; i++) {
3943 View v = getChildAt(i);
3944 if (child == v) {
3945 return notGoneIndex;
3946 }
3947 if (v.getVisibility() != View.GONE) {
3948 notGoneIndex++;
3949 }
3950 }
3951 return -1;
3952 }
3953
Anthony Chen5e3742e2017-04-07 14:28:44 -07003954 public void setDismissView(@NonNull DismissView dismissView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003955 int index = -1;
3956 if (mDismissView != null) {
3957 index = indexOfChild(mDismissView);
3958 removeView(mDismissView);
3959 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04003960 mDismissView = dismissView;
Selim Cinek01af3342016-02-09 19:25:31 -08003961 addView(mDismissView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04003962 }
3963
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003964 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08003965 int index = -1;
3966 if (mEmptyShadeView != null) {
3967 index = indexOfChild(mEmptyShadeView);
3968 removeView(mEmptyShadeView);
3969 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003970 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08003971 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003972 }
3973
3974 public void updateEmptyShadeView(boolean visible) {
3975 int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
3976 int newVisibility = visible ? VISIBLE : GONE;
3977 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02003978 if (newVisibility != GONE) {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003979 if (mEmptyShadeView.willBeGone()) {
3980 mEmptyShadeView.cancelAnimation();
3981 } else {
3982 mEmptyShadeView.setInvisible();
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003983 }
Julia Reynolds9cf17562018-03-14 09:49:35 -04003984 if (mStatusBar.areNotificationsHidden()) {
3985 mEmptyShadeView.setText(R.string.dnd_suppressing_shade_text);
3986 } else {
3987 mEmptyShadeView.setText(R.string.empty_shade_text);
3988 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02003989 mEmptyShadeView.setVisibility(newVisibility);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003990 mEmptyShadeView.setWillBeGone(false);
3991 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07003992 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003993 } else {
Selim Cinek20867102014-12-10 17:09:17 +01003994 Runnable onFinishedRunnable = new Runnable() {
Jorim Jaggia2052ea2014-08-05 16:22:30 +02003995 @Override
3996 public void run() {
3997 mEmptyShadeView.setVisibility(GONE);
3998 mEmptyShadeView.setWillBeGone(false);
3999 updateContentHeight();
Selim Cinek2cd45df2015-06-09 18:00:07 -07004000 notifyHeightChangeListener(mEmptyShadeView);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004001 }
Selim Cinek20867102014-12-10 17:09:17 +01004002 };
Selim Cineka9d3efd2016-08-16 14:16:59 -07004003 if (mAnimationsEnabled && mIsExpanded) {
Selim Cinek20867102014-12-10 17:09:17 +01004004 mEmptyShadeView.setWillBeGone(true);
4005 mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
4006 } else {
4007 mEmptyShadeView.setInvisible();
4008 onFinishedRunnable.run();
4009 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004010 }
4011 }
4012 }
4013
Dan Sandlereceda3d2014-07-21 15:35:01 -04004014 public void updateDismissView(boolean visible) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004015 if (mDismissView == null) {
4016 return;
4017 }
4018
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004019 int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
Dan Sandlereceda3d2014-07-21 15:35:01 -04004020 int newVisibility = visible ? VISIBLE : GONE;
4021 if (oldVisibility != newVisibility) {
Selim Cinekd2319fb2014-09-01 19:41:54 +02004022 if (newVisibility != GONE) {
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004023 if (mDismissView.willBeGone()) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04004024 mDismissView.cancelAnimation();
4025 } else {
4026 mDismissView.setInvisible();
Dan Sandlereceda3d2014-07-21 15:35:01 -04004027 }
Selim Cinekd2319fb2014-09-01 19:41:54 +02004028 mDismissView.setVisibility(newVisibility);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004029 mDismissView.setWillBeGone(false);
4030 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02004031 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004032 } else {
Selim Cinek7d5f3742014-11-07 18:07:49 +01004033 Runnable dimissHideFinishRunnable = new Runnable() {
Dan Sandlereceda3d2014-07-21 15:35:01 -04004034 @Override
4035 public void run() {
4036 mDismissView.setVisibility(GONE);
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004037 mDismissView.setWillBeGone(false);
4038 updateContentHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02004039 notifyHeightChangeListener(mDismissView);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004040 }
Selim Cinek7d5f3742014-11-07 18:07:49 +01004041 };
Selim Cinek20867102014-12-10 17:09:17 +01004042 if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
Selim Cinek7d5f3742014-11-07 18:07:49 +01004043 mDismissView.setWillBeGone(true);
4044 mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
4045 } else {
4046 dimissHideFinishRunnable.run();
Selim Cinek7d5f3742014-11-07 18:07:49 +01004047 }
Dan Sandlereceda3d2014-07-21 15:35:01 -04004048 }
4049 }
4050 }
4051
4052 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4053 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004054 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004055 handleDismissAllClipping();
4056 }
4057
4058 private void handleDismissAllClipping() {
4059 final int count = getChildCount();
4060 boolean previousChildWillBeDismissed = false;
4061 for (int i = 0; i < count; i++) {
4062 ExpandableView child = (ExpandableView) getChildAt(i);
4063 if (child.getVisibility() == GONE) {
4064 continue;
4065 }
4066 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4067 child.setMinClipTopAmount(child.getClipTopAmount());
4068 } else {
4069 child.setMinClipTopAmount(0);
4070 }
4071 previousChildWillBeDismissed = canChildBeDismissed(child);
4072 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004073 }
4074
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004075 public boolean isDismissViewNotGone() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004076 return mDismissView != null
4077 && mDismissView.getVisibility() != View.GONE
4078 && !mDismissView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004079 }
4080
4081 public boolean isDismissViewVisible() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004082 return mDismissView != null && mDismissView.isVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004083 }
4084
4085 public int getDismissViewHeight() {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004086 return mDismissView == null ? 0 : mDismissView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004087 }
4088
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004089 public int getEmptyShadeViewHeight() {
4090 return mEmptyShadeView.getHeight();
4091 }
4092
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004093 public float getBottomMostNotificationBottom() {
4094 final int count = getChildCount();
4095 float max = 0;
4096 for (int childIdx = 0; childIdx < count; childIdx++) {
4097 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4098 if (child.getVisibility() == GONE) {
4099 continue;
4100 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004101 float bottom = child.getTranslationY() + child.getActualHeight()
4102 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004103 if (bottom > max) {
4104 max = bottom;
4105 }
4106 }
Selim Cinekd2281152015-04-10 14:37:46 -07004107 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004108 }
4109
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004110 public void setStatusBar(StatusBar statusBar) {
4111 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004112 }
4113
Selim Cinekb5605e52015-02-20 18:21:41 +01004114 public void setGroupManager(NotificationGroupManager groupManager) {
4115 this.mGroupManager = groupManager;
4116 }
4117
Selim Cinekd9acca52014-09-01 22:33:25 +02004118 public void onGoToKeyguard() {
Selim Cinek379ff8f2015-02-20 17:03:16 +01004119 requestAnimateEverything();
4120 }
4121
4122 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004123 if (mIsExpanded && mAnimationsEnabled) {
4124 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004125 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004126 requestChildrenUpdate();
4127 }
4128 }
4129
Selim Cinek04fb2582015-06-02 19:58:09 +02004130 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004131 int childCount = getChildCount();
4132 for (int i = childCount - 1; i >= 0; i--) {
4133 ExpandableView child = (ExpandableView) getChildAt(i);
4134 if (child.getVisibility() != View.GONE) {
4135 float childTop = child.getY();
4136 if (childTop > touchY) {
4137 // we are above a notification entirely let's abort
4138 return false;
4139 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004140 boolean belowChild = touchY > childTop + child.getActualHeight()
4141 - child.getClipBottomAmount();
Selim Cinekabf60bb2015-02-20 17:36:10 +01004142 if (child == mDismissView) {
4143 if(!belowChild && !mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
4144 touchY - childTop)) {
4145 // We clicked on the dismiss button
4146 return false;
4147 }
4148 } else if (child == mEmptyShadeView) {
4149 // We arrived at the empty shade view, for which we accept all clicks
4150 return true;
4151 } else if (!belowChild){
4152 // We are on a child
4153 return false;
4154 }
4155 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004156 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004157 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004158 }
4159
Selim Cinekb5605e52015-02-20 18:21:41 +01004160 @Override
4161 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004162 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
4163 && (mIsExpanded || changedRow.isPinned());
Selim Cinekb5605e52015-02-20 18:21:41 +01004164 if (animated) {
4165 mExpandedGroupView = changedRow;
4166 mNeedsAnimation = true;
4167 }
4168 changedRow.setChildrenExpanded(expanded, animated);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07004169 if (!mGroupExpandedForMeasure) {
4170 onHeightChanged(changedRow, false /* needsAnimation */);
4171 }
Mady Mellorb0a82462016-04-30 17:31:02 -07004172 runAfterAnimationFinished(new Runnable() {
4173 @Override
4174 public void run() {
4175 changedRow.onFinishedExpansionChange();
4176 }
4177 });
Selim Cinekb5605e52015-02-20 18:21:41 +01004178 }
4179
4180 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004181 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004182 mStatusBar.requestNotificationUpdate();
Selim Cinekef5127e2015-12-21 16:55:58 -08004183 }
4184
Selim Cinekc22fff62016-05-20 12:44:30 -07004185 /** @hide */
4186 @Override
4187 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4188 super.onInitializeAccessibilityEventInternal(event);
4189 event.setScrollable(mScrollable);
4190 event.setScrollX(mScrollX);
4191 event.setScrollY(mOwnScrollY);
4192 event.setMaxScrollX(mScrollX);
4193 event.setMaxScrollY(getScrollRange());
4194 }
4195
4196 @Override
4197 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4198 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004199 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004200 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004201 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004202 info.addAction(
4203 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4204 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4205 }
Selim Cinekef406062016-09-29 17:33:13 -07004206 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004207 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4208 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4209 }
4210 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004211 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4212 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004213 }
4214
4215 /** @hide */
4216 @Override
4217 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4218 if (super.performAccessibilityActionInternal(action, arguments)) {
4219 return true;
4220 }
4221 if (!isEnabled()) {
4222 return false;
4223 }
4224 int direction = -1;
4225 switch (action) {
4226 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4227 // fall through
4228 case android.R.id.accessibilityActionScrollDown:
4229 direction = 1;
4230 // fall through
4231 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4232 // fall through
4233 case android.R.id.accessibilityActionScrollUp:
4234 final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
Selim Cinekad7fac02016-10-18 17:09:15 -07004235 - mShelf.getIntrinsicHeight();
Selim Cinekc22fff62016-05-20 12:44:30 -07004236 final int targetScrollY = Math.max(0,
4237 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4238 if (targetScrollY != mOwnScrollY) {
4239 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
Selim Cinek9212de82017-02-06 16:04:28 -08004240 animateScroll();
Selim Cinekc22fff62016-05-20 12:44:30 -07004241 return true;
4242 }
4243 break;
4244 }
4245 return false;
4246 }
4247
Selim Cinekef5127e2015-12-21 16:55:58 -08004248 @Override
Selim Cinek2a739342016-03-17 10:28:55 -07004249 public void onGroupsChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004250 mStatusBar.requestNotificationUpdate();
Selim Cinekb5605e52015-02-20 18:21:41 +01004251 }
4252
4253 public void generateChildOrderChangedEvent() {
4254 if (mIsExpanded && mAnimationsEnabled) {
4255 mGenerateChildOrderChangedEvent = true;
4256 mNeedsAnimation = true;
4257 requestChildrenUpdate();
4258 }
4259 }
4260
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004261 @Override
4262 public int getContainerChildCount() {
4263 return getChildCount();
4264 }
4265
4266 @Override
4267 public View getContainerChildAt(int i) {
4268 return getChildAt(i);
4269 }
4270
4271 @Override
4272 public void removeContainerView(View v) {
4273 removeView(v);
4274 }
4275
4276 @Override
4277 public void addContainerView(View v) {
4278 addView(v);
4279 }
4280
Selim Cinek684a4422015-04-15 16:18:39 -07004281 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004282 mAnimationFinishedRunnables.add(runnable);
4283 }
4284
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004285 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004286 mHeadsUpManager = headsUpManager;
4287 mAmbientState.setHeadsUpManager(headsUpManager);
Selim Cinek29aab962018-02-27 17:05:45 -08004288 mHeadsUpManager.addListener(mRoundnessManager);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004289 }
4290
4291 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07004292 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004293 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
4294 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07004295 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004296 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07004297 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004298 requestChildrenUpdate();
4299 }
4300 }
4301
4302 public void setShadeExpanded(boolean shadeExpanded) {
4303 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07004304 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004305 }
4306
Selim Cineka59ecc32015-04-07 10:51:49 -07004307 /**
4308 * Set the boundary for the bottom heads up position. The heads up will always be above this
4309 * position.
4310 *
4311 * @param height the height of the screen
4312 * @param bottomBarHeight the height of the bar on the bottom
4313 */
4314 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
4315 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
4316 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004317 requestChildrenUpdate();
4318 }
4319
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004320 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
4321 mTrackingHeadsUp = row != null;
4322 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004323 }
4324
Selim Cinekaac93252015-04-14 20:04:12 -07004325 public void setScrimController(ScrimController scrimController) {
4326 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07004327 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07004328 }
4329
Selim Cinekbbc580b2015-06-03 14:11:03 +02004330 public void forceNoOverlappingRendering(boolean force) {
4331 mForceNoOverlappingRendering = force;
4332 }
4333
4334 @Override
4335 public boolean hasOverlappingRendering() {
4336 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
4337 }
4338
Selim Cinek6811d722016-01-19 17:53:12 -08004339 public void setAnimationRunning(boolean animationRunning) {
4340 if (animationRunning != mAnimationRunning) {
4341 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07004342 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004343 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07004344 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08004345 }
4346 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08004347 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08004348 }
4349 }
4350
Selim Cinek3776fe02016-02-04 13:32:43 -08004351 public boolean isExpanded() {
4352 return mIsExpanded;
4353 }
4354
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004355 public void setPulsing(boolean pulsing, int clockBottom) {
4356 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01004357 return;
4358 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004359 mPulsing = pulsing;
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004360 mClockBottom = clockBottom;
Selim Cinekebf42342017-07-13 15:46:10 +02004361 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004362 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08004363 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004364 updateContentHeight();
4365 notifyHeightChangeListener(mShelf);
Adrian Roosd83e9992017-03-16 15:17:57 -07004366 requestChildrenUpdate();
Selim Cinekcd5b22f2016-03-08 16:15:41 -08004367 }
4368
Selim Cinek07304f5222016-05-19 18:31:36 -07004369 public void setFadingOut(boolean fadingOut) {
4370 if (fadingOut != mFadingOut) {
4371 mFadingOut = fadingOut;
4372 updateFadingState();
Selim Cinek31d37b92016-04-26 09:56:42 -07004373 }
4374 }
4375
Selim Cinek48ff9b42016-11-09 19:31:51 -08004376 public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
4377 if (mScrimController == null) {
4378 // we're not set up yet.
4379 return;
4380 }
4381 if (parentNotFullyVisible != mParentNotFullyVisible) {
4382 mParentNotFullyVisible = parentNotFullyVisible;
Selim Cinek07304f5222016-05-19 18:31:36 -07004383 updateFadingState();
4384 }
4385 }
4386
4387 private void updateFadingState() {
Selim Cinek9bfba9c2016-08-08 15:20:06 -07004388 applyCurrentBackgroundBounds();
Selim Cinek07304f5222016-05-19 18:31:36 -07004389 updateSrcDrawing();
4390 }
4391
Selim Cinek31d37b92016-04-26 09:56:42 -07004392 @Override
4393 public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
4394 super.setAlpha(alpha);
Selim Cinek07304f5222016-05-19 18:31:36 -07004395 setFadingOut(alpha != 1.0f);
Selim Cinek31d37b92016-04-26 09:56:42 -07004396 }
4397
Selim Cinekbc243a92016-09-27 16:35:13 -07004398 public void setQsExpanded(boolean qsExpanded) {
4399 mQsExpanded = qsExpanded;
4400 updateAlgorithmLayoutMinHeight();
4401 }
4402
Selim Cinekef406062016-09-29 17:33:13 -07004403 public void setOwnScrollY(int ownScrollY) {
4404 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08004405 // We still want to call the normal scrolled changed for accessibility reasons
4406 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07004407 mOwnScrollY = ownScrollY;
4408 updateForwardAndBackwardScrollability();
Selim Cinek9212de82017-02-06 16:04:28 -08004409 requestChildrenUpdate();
Selim Cinekef406062016-09-29 17:33:13 -07004410 }
4411 }
4412
Selim Cinek281c2022016-10-13 19:14:43 -07004413 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07004414 int index = -1;
4415 if (mShelf != null) {
4416 index = indexOfChild(mShelf);
4417 removeView(mShelf);
4418 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08004419 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07004420 addView(mShelf, index);
4421 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07004422 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07004423 shelf.bind(mAmbientState, this);
Selim Cinek281c2022016-10-13 19:14:43 -07004424 }
4425
4426 public NotificationShelf getNotificationShelf() {
4427 return mShelf;
4428 }
4429
Selim Cinekad7fac02016-10-18 17:09:15 -07004430 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
4431 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
4432 mMaxDisplayedNotifications = maxDisplayedNotifications;
4433 updateContentHeight();
4434 notifyHeightChangeListener(mShelf);
4435 }
4436 }
4437
Selim Cinek48ff9b42016-11-09 19:31:51 -08004438 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07004439 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08004440 }
4441
Selim Cinekcafa87f2016-10-26 17:00:17 -07004442 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
4443 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
4444 updateClipping();
4445 }
4446
4447 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
4448 mHeadsUpAnimatingAway = headsUpAnimatingAway;
4449 updateClipping();
4450 }
4451
Selim Cinek355652a2016-12-07 13:32:12 -08004452 public void setStatusBarState(int statusBarState) {
4453 mStatusBarState = statusBarState;
4454 mAmbientState.setStatusBarState(statusBarState);
4455 }
4456
Selim Cinekd5ab6452016-12-08 16:34:00 -08004457 public void setExpandingVelocity(float expandingVelocity) {
4458 mAmbientState.setExpandingVelocity(expandingVelocity);
4459 }
4460
Selim Cinekfcff4c62016-12-27 14:26:06 +01004461 public float getOpeningHeight() {
4462 if (mEmptyShadeView.getVisibility() == GONE) {
4463 return getMinExpansionHeight();
4464 } else {
4465 return getAppearEndPosition();
4466 }
4467 }
4468
4469 public void setIsFullWidth(boolean isFullWidth) {
4470 mAmbientState.setPanelFullWidth(isFullWidth);
4471 }
4472
Selim Cinekec29d342017-05-05 18:31:49 -07004473 public void setUnlockHintRunning(boolean running) {
4474 mAmbientState.setUnlockHintRunning(running);
4475 }
4476
Selim Cinek5cf1d052017-06-01 17:36:46 -07004477 public void setQsCustomizerShowing(boolean isShowing) {
4478 mAmbientState.setQsCustomizerShowing(isShowing);
4479 requestChildrenUpdate();
4480 }
4481
4482 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
4483 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
4484 }
4485
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004486 public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
4487 mAntiBurnInOffsetX = antiBurnInOffsetX;
4488 updateAntiBurnInTranslation();
Adrian Roosdc747bd2017-06-01 16:09:15 -07004489 }
4490
Selim Cinek707e2072017-06-30 18:32:40 +02004491 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4492 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
4493 + " alpha:%f scrollY:%d]",
4494 this.getClass().getSimpleName(),
yoshiki iguchi4e30e762018-02-06 12:09:23 +09004495 mPulsing ? "T":"f",
Selim Cinek707e2072017-06-30 18:32:40 +02004496 mAmbientState.isQsCustomizerShowing() ? "T":"f",
4497 getVisibility() == View.VISIBLE ? "visible"
4498 : getVisibility() == View.GONE ? "gone"
4499 : "invisible",
4500 getAlpha(),
4501 mAmbientState.getScrollY()));
4502 }
4503
Selim Cinek7103fd42016-05-09 22:22:33 -04004504 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08004505 * Add a listener whenever the expanded height changes. The first value passed as an argument
4506 * is the expanded height and the second one is the appearFraction.
4507 *
4508 * @param listener the listener to notify.
4509 */
4510 public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
4511 mExpandedHeightListeners.add(listener);
4512 }
4513
4514 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004515 * A listener that is notified when the empty space below the notifications is clicked on
4516 */
4517 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004518 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004519 }
4520
4521 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02004522 * A listener that gets notified when the overscroll at the top has changed.
4523 */
4524 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02004525
4526 /**
4527 * Notifies a listener that the overscroll has changed.
4528 *
4529 * @param amount the amount of overscroll, in pixels
4530 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
4531 * unrubberbanded motion to directly expand overscroll view (e.g expand
4532 * QS)
4533 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004534 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02004535
4536 /**
4537 * Notify a listener that the scroller wants to escape from the scrolling motion and
4538 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
4539 *
4540 * @param velocity The velocity that the Scroller had when over flinging
4541 * @param open Should the fling open or close the overscroll view.
4542 */
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004543 void flingTopOverscroll(float velocity, boolean open);
Jorim Jaggi290600a2014-05-30 17:02:20 +02004544 }
4545
Mady Mellor95d743c2017-01-10 12:05:27 -08004546 private class NotificationSwipeHelper extends SwipeHelper
4547 implements NotificationSwipeActionHelper {
4548 private static final long COVER_MENU_DELAY = 4000;
Mady Mellor97c8df42016-03-22 18:09:39 -07004549 private Runnable mFalsingCheck;
Mady Mellor4b80b102016-01-22 08:03:58 -08004550 private Handler mHandler;
Mady Mellor4b80b102016-01-22 08:03:58 -08004551
4552 public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
4553 super(swipeDirection, callback, context);
4554 mHandler = new Handler();
Mady Mellor97c8df42016-03-22 18:09:39 -07004555 mFalsingCheck = new Runnable() {
4556 @Override
4557 public void run() {
Mady Mellor95d743c2017-01-10 12:05:27 -08004558 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor97c8df42016-03-22 18:09:39 -07004559 }
4560 };
Mady Mellor4b80b102016-01-22 08:03:58 -08004561 }
4562
4563 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004564 public void onDownUpdate(View currView, MotionEvent ev) {
Mady Mellor4b80b102016-01-22 08:03:58 -08004565 mTranslatingParentView = currView;
Mady Mellor95d743c2017-01-10 12:05:27 -08004566 if (mCurrMenuRow != null) {
4567 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004568 }
Mady Mellor89e15ec2017-06-28 17:08:21 -07004569 mCurrMenuRow = null;
Mady Mellor97c8df42016-03-22 18:09:39 -07004570 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor4b80b102016-01-22 08:03:58 -08004571
Mady Mellor95d743c2017-01-10 12:05:27 -08004572 // Slide back any notifications that might be showing a menu
4573 resetExposedMenuView(true /* animate */, false /* force */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004574
4575 if (currView instanceof ExpandableNotificationRow) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004576 ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
4577 mCurrMenuRow = row.createMenu();
4578 mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
4579 mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
Mady Mellor89e15ec2017-06-28 17:08:21 -07004580 mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004581 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004582 }
4583
4584 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004585 public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
Mady Mellor97c8df42016-03-22 18:09:39 -07004586 mHandler.removeCallbacks(mFalsingCheck);
Mady Mellor95d743c2017-01-10 12:05:27 -08004587 if (mCurrMenuRow != null) {
4588 mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
Mady Mellor4b80b102016-01-22 08:03:58 -08004589 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004590 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004591
Mady Mellor95d743c2017-01-10 12:05:27 -08004592 @Override
4593 public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
4594 float translation) {
4595 if (mCurrMenuRow != null) {
4596 return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
Mady Mellor4b80b102016-01-22 08:03:58 -08004597 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004598 return false;
Mady Mellor4b80b102016-01-22 08:03:58 -08004599 }
4600
4601 @Override
Mady Mellordc6c97d2016-03-31 14:18:35 -07004602 public void dismissChild(final View view, float velocity,
4603 boolean useAccelerateInterpolator) {
4604 super.dismissChild(view, velocity, useAccelerateInterpolator);
Selim Cinekd1395642016-04-28 12:22:42 -07004605 if (mIsExpanded) {
4606 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
4607 // of the panel early.
4608 handleChildDismissed(view);
4609 }
Eliot Courtney47098cb2017-10-18 17:30:30 +09004610 mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
4611 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4612 false /* resetMenu */);
Mady Mellor95d743c2017-01-10 12:05:27 -08004613 handleMenuCoveredOrDismissed();
Mady Mellor4b80b102016-01-22 08:03:58 -08004614 }
4615
4616 @Override
4617 public void snapChild(final View animView, final float targetLeft, float velocity) {
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004618 super.snapChild(animView, targetLeft, velocity);
4619 onDragCancelled(animView);
4620 if (targetLeft == 0) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004621 handleMenuCoveredOrDismissed();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004622 }
4623 }
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004624
4625 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004626 public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
4627 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004628 }
4629
Mady Mellorbd707492017-05-10 17:51:25 -07004630 public boolean isFalseGesture(MotionEvent ev) {
4631 return super.isFalseGesture(ev);
4632 }
4633
Mady Mellor95d743c2017-01-10 12:05:27 -08004634 private void handleMenuCoveredOrDismissed() {
4635 if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
4636 mMenuExposedView = null;
Mady Mellor3a5e8dd2016-03-12 00:13:23 +00004637 }
4638 }
4639
Mady Mellor4b80b102016-01-22 08:03:58 -08004640 @Override
Mady Mellor4b80b102016-01-22 08:03:58 -08004641 public Animator getViewTranslationAnimator(View v, float target,
4642 AnimatorUpdateListener listener) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004643 if (v instanceof ExpandableNotificationRow) {
Mady Mellor34958fa2016-02-23 09:52:17 -08004644 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
4645 } else {
4646 return super.getViewTranslationAnimator(v, target, listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08004647 }
Mady Mellor4b80b102016-01-22 08:03:58 -08004648 }
4649
4650 @Override
4651 public void setTranslation(View v, float translate) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004652 ((ExpandableView) v).setTranslation(translate);
Mady Mellor4b80b102016-01-22 08:03:58 -08004653 }
4654
4655 @Override
4656 public float getTranslation(View v) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07004657 return ((ExpandableView) v).getTranslation();
Mady Mellor4b80b102016-01-22 08:03:58 -08004658 }
4659
Mady Mellor95d743c2017-01-10 12:05:27 -08004660 @Override
4661 public void dismiss(View animView, float velocity) {
4662 dismissChild(animView, velocity,
4663 !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
4664 }
4665
4666 @Override
4667 public void snap(View animView, float targetLeft, float velocity) {
4668 snapChild(animView, targetLeft, velocity);
4669 }
4670
4671 @Override
4672 public boolean swipedFarEnough(float translation, float viewSize) {
4673 return swipedFarEnough();
4674 }
4675
4676 @Override
4677 public boolean swipedFastEnough(float translation, float velocity) {
4678 return swipedFastEnough();
4679 }
4680
4681 @Override
4682 public float getMinDismissVelocity() {
4683 return getEscapeVelocity();
4684 }
4685
4686 public void onMenuShown(View animView) {
4687 onDragCancelled(animView);
4688
4689 // If we're on the lockscreen we want to false this.
4690 if (isAntiFalsingNeeded()) {
4691 mHandler.removeCallbacks(mFalsingCheck);
4692 mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
4693 }
4694 }
4695
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004696 public void closeControlsIfOutsideTouch(MotionEvent ev) {
Eliot Courtney47098cb2017-10-18 17:30:30 +09004697 NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004698 View view = null;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004699 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4700 // Only close visible guts if they're not a leavebehind.
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004701 view = guts;
Mady Mellor95d743c2017-01-10 12:05:27 -08004702 } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004703 && mTranslatingParentView != null) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004704 // Checking menu
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004705 view = mTranslatingParentView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004706 }
Mady Mellorc2dbe492017-03-30 13:22:03 -07004707 if (view != null && !isTouchInView(ev, view)) {
4708 // Touch was outside visible guts / menu notification, close what's visible
Eliot Courtney47098cb2017-10-18 17:30:30 +09004709 mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
4710 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4711 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004712 resetExposedMenuView(true /* animate */, true /* force */);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004713 }
4714 }
4715
Mady Mellor95d743c2017-01-10 12:05:27 -08004716 public void resetExposedMenuView(boolean animate, boolean force) {
4717 if (mMenuExposedView == null
4718 || (!force && mMenuExposedView == mTranslatingParentView)) {
4719 // If no menu is showing or it's showing for this view we do nothing.
Mady Mellor4b80b102016-01-22 08:03:58 -08004720 return;
4721 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004722 final View prevMenuExposedView = mMenuExposedView;
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004723 if (animate) {
Mady Mellor95d743c2017-01-10 12:05:27 -08004724 Animator anim = getViewTranslationAnimator(prevMenuExposedView,
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004725 0 /* leftTarget */, null /* updateListener */);
4726 if (anim != null) {
4727 anim.start();
4728 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004729 } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
Selim Cinekb2e0f332017-08-18 12:24:38 -07004730 ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
4731 if (!row.isRemoved()) {
4732 row.resetTranslation();
4733 }
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004734 }
Mady Mellor95d743c2017-01-10 12:05:27 -08004735 mMenuExposedView = null;
Mady Mellor4b80b102016-01-22 08:03:58 -08004736 }
4737 }
4738
Mady Mellorc2dbe492017-03-30 13:22:03 -07004739 private boolean isTouchInView(MotionEvent ev, View view) {
4740 if (view == null) {
4741 return false;
4742 }
4743 final int height = (view instanceof ExpandableView)
4744 ? ((ExpandableView) view).getActualHeight()
4745 : view.getHeight();
4746 final int rx = (int) ev.getRawX();
4747 final int ry = (int) ev.getRawY();
4748 view.getLocationOnScreen(mTempInt2);
4749 final int x = mTempInt2[0];
4750 final int y = mTempInt2[1];
4751 Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
4752 boolean ret = rect.contains(rx, ry);
4753 return ret;
4754 }
4755
Selim Cinek33223572016-02-19 19:32:22 -08004756 private void updateContinuousShadowDrawing() {
4757 boolean continuousShadowUpdate = mAnimationRunning
4758 || !mAmbientState.getDraggedViews().isEmpty();
4759 if (continuousShadowUpdate != mContinuousShadowUpdate) {
4760 if (continuousShadowUpdate) {
4761 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
4762 } else {
4763 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
4764 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07004765 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08004766 }
4767 }
4768
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004769 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -08004770 public void resetExposedMenuView(boolean animate, boolean force) {
4771 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07004772 }
4773
4774 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4775 mSwipeHelper.closeControlsIfOutsideTouch(ev);
4776 }
4777
Jorim Jaggi0dd68812014-05-01 19:17:37 +02004778 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02004779
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004780 static AnimationFilter[] FILTERS = new AnimationFilter[] {
4781
4782 // ANIMATION_TYPE_ADD
4783 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004784 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004785 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004786 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004787 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004788 .animateZ()
4789 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004790
4791 // ANIMATION_TYPE_REMOVE
4792 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004793 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004794 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004795 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004796 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004797 .animateZ()
4798 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004799
4800 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4801 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004802 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004803 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004804 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004805 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004806 .animateZ()
4807 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004808
4809 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4810 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004811 .animateShadowAlpha()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004812 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004813 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004814 .animateY()
4815 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004816 .animateZ(),
4817
4818 // ANIMATION_TYPE_START_DRAG
4819 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004820 .animateShadowAlpha(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004821
4822 // ANIMATION_TYPE_SNAP_BACK
4823 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004824 .animateShadowAlpha()
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004825 .animateHeight(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004826
4827 // ANIMATION_TYPE_ACTIVATED_CHILD
4828 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004829 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004830
4831 // ANIMATION_TYPE_DIMMED
4832 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004833 .animateDimmed(),
4834
4835 // ANIMATION_TYPE_CHANGE_POSITION
4836 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004837 .animateAlpha() // maybe the children change positions
4838 .animateShadowAlpha()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004839 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02004840 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004841 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04004842 .animateZ(),
4843
4844 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004845 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004846
4847 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4848 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004849 .animateShadowAlpha()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004850 .animateHeight()
4851 .animateTopInset()
4852 .animateY()
4853 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02004854 .animateZ()
4855 .hasDelays(),
4856
4857 // ANIMATION_TYPE_HIDE_SENSITIVE
4858 new AnimationFilter()
4859 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02004860
4861 // ANIMATION_TYPE_VIEW_RESIZE
4862 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004863 .animateShadowAlpha()
Selim Cineka5e211b2014-08-11 17:35:48 +02004864 .animateHeight()
4865 .animateTopInset()
4866 .animateY()
4867 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02004868
Selim Cinekb5605e52015-02-20 18:21:41 +01004869 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
4870 new AnimationFilter()
4871 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004872 .animateShadowAlpha()
Selim Cinekb5605e52015-02-20 18:21:41 +01004873 .animateHeight()
4874 .animateTopInset()
4875 .animateY()
4876 .animateZ(),
4877
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004878 // ANIMATION_TYPE_HEADS_UP_APPEAR
4879 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004880 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004881 .animateHeight()
4882 .animateTopInset()
4883 .animateY()
4884 .animateZ(),
4885
4886 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4887 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004888 .animateShadowAlpha()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004889 .animateHeight()
4890 .animateTopInset()
4891 .animateY()
4892 .animateZ(),
4893
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004894 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4895 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004896 .animateShadowAlpha()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004897 .animateHeight()
4898 .animateTopInset()
4899 .animateY()
4900 .animateZ()
4901 .hasDelays(),
4902
Selim Cineka59ecc32015-04-07 10:51:49 -07004903 // ANIMATION_TYPE_HEADS_UP_OTHER
4904 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004905 .animateShadowAlpha()
Selim Cineka59ecc32015-04-07 10:51:49 -07004906 .animateHeight()
4907 .animateTopInset()
4908 .animateY()
4909 .animateZ(),
4910
Selim Cinekd9acca52014-09-01 22:33:25 +02004911 // ANIMATION_TYPE_EVERYTHING
4912 new AnimationFilter()
4913 .animateAlpha()
Selim Cinek277a8aa2016-01-22 12:12:37 -08004914 .animateShadowAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02004915 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02004916 .animateDimmed()
4917 .animateHideSensitive()
4918 .animateHeight()
4919 .animateTopInset()
4920 .animateY()
4921 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004922 };
4923
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004924 static int[] LENGTHS = new int[] {
4925
4926 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004927 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004928
4929 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004930 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004931
4932 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
4933 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4934
4935 // ANIMATION_TYPE_TOP_PADDING_CHANGED
4936 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4937
4938 // ANIMATION_TYPE_START_DRAG
4939 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4940
4941 // ANIMATION_TYPE_SNAP_BACK
4942 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4943
4944 // ANIMATION_TYPE_ACTIVATED_CHILD
4945 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
4946
4947 // ANIMATION_TYPE_DIMMED
4948 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004949
4950 // ANIMATION_TYPE_CHANGE_POSITION
4951 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04004952
4953 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07004954 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004955
4956 // ANIMATION_TYPE_GO_TO_FULL_SHADE
4957 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02004958
4959 // ANIMATION_TYPE_HIDE_SENSITIVE
4960 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02004961
4962 // ANIMATION_TYPE_VIEW_RESIZE
4963 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02004964
Selim Cinekb5605e52015-02-20 18:21:41 +01004965 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08004966 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01004967
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004968 // ANIMATION_TYPE_HEADS_UP_APPEAR
4969 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
4970
4971 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
4972 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4973
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07004974 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4975 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
4976
Selim Cineka59ecc32015-04-07 10:51:49 -07004977 // ANIMATION_TYPE_HEADS_UP_OTHER
4978 StackStateAnimator.ANIMATION_DURATION_STANDARD,
4979
Selim Cinekd9acca52014-09-01 22:33:25 +02004980 // ANIMATION_TYPE_EVERYTHING
4981 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02004982 };
4983
Selim Cinek8efa6dd2014-05-19 16:27:37 +02004984 static final int ANIMATION_TYPE_ADD = 0;
4985 static final int ANIMATION_TYPE_REMOVE = 1;
4986 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
4987 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
4988 static final int ANIMATION_TYPE_START_DRAG = 4;
4989 static final int ANIMATION_TYPE_SNAP_BACK = 5;
4990 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
4991 static final int ANIMATION_TYPE_DIMMED = 7;
4992 static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
John Spurlockbf370992014-06-17 13:58:31 -04004993 static final int ANIMATION_TYPE_DARK = 9;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004994 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
Jorim Jaggiae441282014-08-01 02:45:18 +02004995 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
Selim Cineka5e211b2014-08-11 17:35:48 +02004996 static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
Selim Cinekb5605e52015-02-20 18:21:41 +01004997 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004998 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
4999 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005000 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
5001 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
5002 static final int ANIMATION_TYPE_EVERYTHING = 18;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005003
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005004 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
5005 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
5006
Selim Cinek572bbd42014-04-25 16:43:27 +02005007 final long eventStartTime;
5008 final View changingView;
5009 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005010 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005011 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005012 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01005013 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07005014 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02005015
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005016 AnimationEvent(View view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005017 this(view, type, LENGTHS[type]);
5018 }
5019
Adrian Roos28f90c72017-05-08 17:24:26 -07005020 AnimationEvent(View view, int type, AnimationFilter filter) {
5021 this(view, type, LENGTHS[type], filter);
5022 }
5023
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005024 AnimationEvent(View view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07005025 this(view, type, length, FILTERS[type]);
5026 }
5027
5028 AnimationEvent(View view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02005029 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
5030 changingView = view;
5031 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005032 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07005033 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005034 }
5035
5036 /**
5037 * Combines the length of several animation events into a single value.
5038 *
5039 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005040 * @return The combined length. Depending on the event types, this might be the maximum of
5041 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005042 */
5043 static long combineLength(ArrayList<AnimationEvent> events) {
5044 long length = 0;
5045 int size = events.size();
5046 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005047 AnimationEvent event = events.get(i);
5048 length = Math.max(length, event.length);
5049 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
5050 return event.length;
5051 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005052 }
5053 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02005054 }
5055 }
Selim Cinek67b22602014-03-10 15:40:16 +01005056}