blob: fbf1e310abf2eba87fe5d615b3a27dddb98bd453 [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
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.stack;
Selim Cinek67b22602014-03-10 15:40:16 +010018
Gus Prevas59ec2ff2018-12-28 16:20:28 -050019import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
Gus Prevase83700cb2018-12-14 11:42:51 -050020import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
Gus Prevas0fa58d62019-01-11 13:58:40 -050021import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
Gus Prevase83700cb2018-12-14 11:42:51 -050022import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
Gus Prevas33619af2018-10-26 15:40:27 -040023import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
Gus Prevas59ec2ff2018-12-28 16:20:28 -050024import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
Selim Cinek2627d722018-01-19 12:16:49 -080025
Selim Cinek614576e2016-01-20 10:54:09 -080026import android.animation.Animator;
27import android.animation.AnimatorListenerAdapter;
Selim Cinekd35c2792016-01-21 13:20:57 -080028import android.animation.TimeAnimator;
29import android.animation.ValueAnimator;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040030import android.annotation.NonNull;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010031import android.annotation.Nullable;
Jason Monke59dc402018-08-16 12:05:01 -040032import android.app.WallpaperManager;
Selim Cinek67b22602014-03-10 15:40:16 +010033import android.content.Context;
Jason Monke59dc402018-08-16 12:05:01 -040034import android.content.Intent;
Selim Cinek67b22602014-03-10 15:40:16 +010035import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080036import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010037import android.graphics.Canvas;
Lucas Dupind285cf02018-01-18 09:18:23 -080038import android.graphics.Color;
Selim Cinek67b22602014-03-10 15:40:16 +010039import android.graphics.Paint;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010040import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080041import android.graphics.PorterDuff;
42import android.graphics.PorterDuffXfermode;
43import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070044import android.os.Bundle;
Jason Monke59dc402018-08-16 12:05:01 -040045import android.os.ServiceManager;
46import android.provider.Settings;
Mady Mellor95d743c2017-01-10 12:05:27 -080047import android.service.notification.StatusBarNotification;
Selim Cinek67b22602014-03-10 15:40:16 +010048import android.util.AttributeSet;
Jason Monke59dc402018-08-16 12:05:01 -040049import android.util.DisplayMetrics;
Selim Cinek67b22602014-03-10 15:40:16 +010050import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080051import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070052import android.util.Pair;
Lucas Dupine17ce522017-07-17 15:45:06 -070053import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070054import android.view.InputDevice;
Jason Monke59dc402018-08-16 12:05:01 -040055import android.view.LayoutInflater;
Selim Cinek67b22602014-03-10 15:40:16 +010056import android.view.MotionEvent;
57import android.view.VelocityTracker;
58import android.view.View;
59import android.view.ViewConfiguration;
60import android.view.ViewGroup;
Selim Cinek343e6e22014-04-11 21:23:30 +020061import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070062import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070063import android.view.accessibility.AccessibilityEvent;
64import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020065import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080066import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010067import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070068import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070069
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040070import com.android.internal.annotations.VisibleForTesting;
71import com.android.internal.graphics.ColorUtils;
Mady Mellora41587b2016-02-11 18:43:06 -080072import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010073import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monke59dc402018-08-16 12:05:01 -040074import com.android.internal.statusbar.IStatusBarService;
Lucas Dupin4e023812018-04-02 21:19:23 -070075import com.android.keyguard.KeyguardSliceView;
Lucas Dupine17ce522017-07-17 15:45:06 -070076import com.android.settingslib.Utils;
Rohan Shah524cf7b2018-03-15 14:40:02 -070077import com.android.systemui.Dependency;
Jason Monke59dc402018-08-16 12:05:01 -040078import com.android.systemui.Dumpable;
Selim Cinek67b22602014-03-10 15:40:16 +010079import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080080import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010081import com.android.systemui.R;
82import com.android.systemui.SwipeHelper;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070083import com.android.systemui.classifier.FalsingManager;
Jason Monke59dc402018-08-16 12:05:01 -040084import com.android.systemui.colorextraction.SysuiColorExtractor;
Mady Mellor95d743c2017-01-10 12:05:27 -080085import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
86import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040087import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
Mady Mellor95d743c2017-01-10 12:05:27 -080088import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Beverly8fdb5332019-02-04 14:29:49 -050089import com.android.systemui.plugins.statusbar.StatusBarStateController;
90import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Jason Monke59dc402018-08-16 12:05:01 -040091import com.android.systemui.statusbar.CommandQueue;
92import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
Jorim Jaggia2052ea2014-08-05 16:22:30 +020093import com.android.systemui.statusbar.EmptyShadeView;
Jason Monk297c04e2018-08-23 17:16:59 -040094import com.android.systemui.statusbar.NotificationLockscreenUserManager;
95import com.android.systemui.statusbar.NotificationRemoteInputManager;
96import com.android.systemui.statusbar.NotificationShelf;
97import com.android.systemui.statusbar.RemoteInputController;
98import com.android.systemui.statusbar.StatusBarState;
Beverly8fdb5332019-02-04 14:29:49 -050099import com.android.systemui.statusbar.SysuiStatusBarStateController;
Jason Monk297c04e2018-08-23 17:16:59 -0400100import com.android.systemui.statusbar.notification.FakeShadowView;
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500101import com.android.systemui.statusbar.notification.NotificationEntryListener;
Jason Monk297c04e2018-08-23 17:16:59 -0400102import com.android.systemui.statusbar.notification.NotificationEntryManager;
103import com.android.systemui.statusbar.notification.NotificationUtils;
104import com.android.systemui.statusbar.notification.ShadeViewRefactor;
105import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
Jason Monk297c04e2018-08-23 17:16:59 -0400106import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -0500107import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Jason Monk297c04e2018-08-23 17:16:59 -0400108import com.android.systemui.statusbar.notification.logging.NotificationLogger;
109import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
Rohan Shah20790b82018-07-02 17:21:04 -0700110import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
111import com.android.systemui.statusbar.notification.row.ExpandableView;
112import com.android.systemui.statusbar.notification.row.FooterView;
113import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
Rohan Shah20790b82018-07-02 17:21:04 -0700114import com.android.systemui.statusbar.notification.row.NotificationGuts;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400115import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
Rohan Shah20790b82018-07-02 17:21:04 -0700116import com.android.systemui.statusbar.notification.row.NotificationSnooze;
117import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800118import com.android.systemui.statusbar.phone.DozeParameters;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700119import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900120import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400121import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
Jason Monke59dc402018-08-16 12:05:01 -0400122import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
Selim Cinekb5605e52015-02-20 18:21:41 +0100123import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monke59dc402018-08-16 12:05:01 -0400124import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700125import com.android.systemui.statusbar.phone.NotificationIconAreaController;
Jason Monke59dc402018-08-16 12:05:01 -0400126import com.android.systemui.statusbar.phone.NotificationPanelView;
Selim Cinekaac93252015-04-14 20:04:12 -0700127import com.android.systemui.statusbar.phone.ScrimController;
Jason Monk297c04e2018-08-23 17:16:59 -0400128import com.android.systemui.statusbar.phone.ShadeController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800129import com.android.systemui.statusbar.phone.StatusBar;
Jason Monke59dc402018-08-16 12:05:01 -0400130import com.android.systemui.statusbar.policy.ConfigurationController;
131import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900132import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100133import com.android.systemui.statusbar.policy.ScrollAdapter;
Gus Prevas33619af2018-10-26 15:40:27 -0400134import com.android.systemui.tuner.TunerService;
Selim Cinek67b22602014-03-10 15:40:16 +0100135
Selim Cinek707e2072017-06-30 18:32:40 +0200136import java.io.FileDescriptor;
137import java.io.PrintWriter;
Selim Cinek572bbd42014-04-25 16:43:27 +0200138import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800139import java.util.Collections;
140import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200141import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800142import java.util.List;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800143import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200144
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500145import javax.inject.Inject;
146import javax.inject.Named;
147
Selim Cinek67b22602014-03-10 15:40:16 +0100148/**
149 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
150 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400151public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter,
152 NotificationListContainer, ConfigurationListener, Dumpable {
Selim Cinek67b22602014-03-10 15:40:16 +0100153
Selim Cinekd35c2792016-01-21 13:20:57 -0800154 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800155 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100156 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200157 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
158 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200159 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100160 /**
161 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
162 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800163 private static final int INVALID_POINTER = -1;
Gus Prevase2d6f042018-10-17 15:25:30 -0400164 static final int NUM_SECTIONS = 2;
165 /**
166 * The distance in pixels between sections when the sections are directly adjacent (no visible
167 * gap is drawn between them). In this case we don't want to round their corners.
168 */
169 private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
Selim Cinek67b22602014-03-10 15:40:16 +0100170
Selim Cinek1408eb52014-06-02 14:45:38 +0200171 private ExpandHelper mExpandHelper;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400172 private final NotificationSwipeHelper mSwipeHelper;
Selim Cinek67b22602014-03-10 15:40:16 +0100173 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800174 private final Paint mBackgroundPaint = new Paint();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700175 private final boolean mShouldDrawNotificationBackground;
Gus Prevas33619af2018-10-26 15:40:27 -0400176 private boolean mLowPriorityBeforeSpeedBump;
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500177 private final boolean mAllowLongPress;
Gus Prevasa18dc572019-01-14 16:11:22 -0500178 private boolean mDismissRtl;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100179
Selim Cinekbc243a92016-09-27 16:35:13 -0700180 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100181 private int mOwnScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -0500182 private View mScrollAnchorView;
183 private int mScrollAnchorViewY;
Selim Cinek67b22602014-03-10 15:40:16 +0100184 private int mMaxLayoutHeight;
185
186 private VelocityTracker mVelocityTracker;
187 private OverScroller mScroller;
Gus Prevascdc98342019-01-14 14:29:44 -0500188 /** Last Y position reported by {@link #mScroller}, used to calculate scroll delta. */
189 private int mLastScrollerY;
190 /**
191 * True if the max position was set to a known position on the last call to {@link #mScroller}.
192 */
193 private boolean mIsScrollerBoundSet;
Ricky Waicd35def2016-05-03 11:07:07 +0100194 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100195 private int mTouchSlop;
196 private int mMinimumVelocity;
197 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100198 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200199 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100200 private boolean mIsBeingDragged;
201 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200202 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700203 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100204 private boolean mTouchIsClick;
205 private float mInitialTouchX;
206 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100207
Selim Cinek67b22602014-03-10 15:40:16 +0100208 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100209 private int mContentHeight;
Lucas Dupin60661a62018-04-12 10:50:13 -0700210 private int mIntrinsicContentHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100211 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100212 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800213 private int mIncreasedPaddingBetweenElements;
shawnlin8e4e92c2018-04-12 18:47:24 +0800214 private int mMaxTopPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200215 private int mTopPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700216 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700217 private int mBottomInset = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +0800218 private float mQsExpansionFraction;
Selim Cinek67b22602014-03-10 15:40:16 +0100219
220 /**
221 * The algorithm which calculates the properties for our children
222 */
Muyuan Li87798022016-04-07 17:51:25 -0700223 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100224
Selim Cinek281c2022016-10-13 19:14:43 -0700225 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100226 private NotificationGroupManager mGroupManager;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500227 private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700228 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500229 private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
230 private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200231 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700232 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
233 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200234 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200235 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200236 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800237 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200238
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200239 /**
240 * The raw amount of the overScroll on the top, which is not rubber-banded.
241 */
242 private float mOverScrolledTopPixels;
243
244 /**
245 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
246 */
247 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900248 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200249 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200250 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100251 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200252 private boolean mNeedsAnimation;
253 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200254 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200255 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400256 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100257 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200258 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200259 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200260 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200261 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200262 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700263 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200264 private boolean mExpandingNotification;
265 private boolean mExpandedInThisMotion;
shawnlin8e4e92c2018-04-12 18:47:24 +0800266 private boolean mShouldShowShelfOnly;
Muyuan Li84b45612016-06-01 11:05:08 -0700267 protected boolean mScrollingEnabled;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400268 protected FooterView mFooterView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700269 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400270 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700271 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200272
273 /**
274 * Was the scroller scrolled to the top when the down motion was observed?
275 */
276 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200277 /**
278 * The minimal amount of over scroll which is needed in order to switch to the quick settings
279 * when over scrolling on a expanded card.
280 */
281 private float mMinTopOverScrollToEscape;
282 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700283 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200284 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200285 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700286 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200287 private boolean mNeedViewResizeAnimation;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500288 private ExpandableView mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700289 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700290
Selim Cinek1408eb52014-06-02 14:45:38 +0200291 /**
292 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
293 * This is needed to avoid scrolling too far after the notification was collapsed in the same
294 * motion.
295 */
296 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000297 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700298 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200299
300 /**
301 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
302 * animating.
303 */
304 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700305 private boolean mDisallowDismissInThisMotion;
Selim Cineka59ecc32015-04-07 10:51:49 -0700306 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700307 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200308 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200309 = new ViewTreeObserver.OnPreDrawListener() {
310 @Override
311 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400312 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200313 updateChildren();
314 mChildrenUpdateRequested = false;
315 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200316 return true;
317 }
318 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500319 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100320 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100321 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700322 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek9dd0d042018-05-14 18:12:42 -0700323 private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700324 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
325 = new HashSet<>();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900326 private HeadsUpManagerPhone mHeadsUpManager;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800327 private final NotificationRoundnessManager mRoundnessManager;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700328 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700329 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200330 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700331 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700332 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800333 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700334 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800335 = new ViewTreeObserver.OnPreDrawListener() {
336 @Override
337 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700338 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800339 return true;
340 }
341 };
Gus Prevase2d6f042018-10-17 15:25:30 -0400342 private NotificationSection[] mSections = new NotificationSection[NUM_SECTIONS];
Selim Cinek614576e2016-01-20 10:54:09 -0800343 private boolean mAnimateNextBackgroundTop;
Gus Prevase2d6f042018-10-17 15:25:30 -0400344 private boolean mAnimateNextBackgroundBottom;
345 private boolean mAnimateNextSectionBoundsChange;
Selim Cinekd35c2792016-01-21 13:20:57 -0800346 private int mBgColor;
347 private float mDimAmount;
348 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800349 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400350 private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
Selim Cinekd35c2792016-01-21 13:20:57 -0800351 @Override
352 public void onAnimationEnd(Animator animation) {
353 mDimAnimator = null;
354 }
355 };
356 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
357 = new ValueAnimator.AnimatorUpdateListener() {
358
359 @Override
360 public void onAnimationUpdate(ValueAnimator animation) {
361 setDimAmount((Float) animation.getAnimatedValue());
362 }
363 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700364 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800365 private boolean mContinuousShadowUpdate;
366 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
367 = new ViewTreeObserver.OnPreDrawListener() {
368
369 @Override
370 public boolean onPreDraw() {
371 updateViewShadows();
372 return true;
373 }
374 };
375 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
376 @Override
377 public int compare(ExpandableView view, ExpandableView otherView) {
378 float endY = view.getTranslationY() + view.getActualHeight();
379 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
380 if (endY < otherEndY) {
381 return -1;
382 } else if (endY > otherEndY) {
383 return 1;
384 } else {
385 // The two notifications end at the same location
386 return 0;
387 }
388 }
389 };
Selim Cinek25503252016-03-03 15:31:43 -0800390 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900391 private boolean mPulsing;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700392 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700393 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400394 private View mForcedScroll;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500395 private ExpandableView mNeedingPulseAnimation;
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200396
397 /**
398 * @see #setDarkAmount(float, float)
399 */
400 private float mInterpolatedDarkAmount = 0f;
401
402 /**
403 * @see #setDarkAmount(float, float)
404 */
405 private float mLinearDarkAmount = 0f;
Lucas Dupin439bd442018-06-12 15:05:28 -0700406
407 /**
408 * How fast the background scales in the X direction as a factor of the Y expansion.
409 */
410 private float mBackgroundXFactor = 1f;
Selim Cinek972123d2016-05-03 14:25:58 -0700411
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -0500412 private boolean mSwipingInProgress;
413
Lucas Dupine17ce522017-07-17 15:45:06 -0700414 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700415 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700416 private boolean mForwardScrollable;
417 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700418 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700419 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800420 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700421 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800422 private boolean mNoAmbient;
423 private final Rect mClipRect = new Rect();
424 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700425 private Rect mRequestedClipBounds;
426 private boolean mInHeadsUpPinnedMode;
427 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800428 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100429 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700430 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Gus Prevascdc98342019-01-14 14:29:44 -0500431 private Runnable mReflingAndAnimateScroll = () -> {
432 if (ANCHOR_SCROLLING) {
433 maybeReflingScroller();
434 }
435 animateScroll();
436 };
Selim Cinek0fe07392017-11-09 13:26:34 -0800437 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800438 private int mSidePaddings;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800439 private final Rect mBackgroundAnimationRect = new Rect();
Lucas Dupin0cd882f2018-01-30 12:19:49 -0800440 private int mAntiBurnInOffsetX;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800441 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
442 private int mHeadsUpInset;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700443 private HeadsUpAppearanceController mHeadsUpAppearanceController;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700444 private NotificationIconAreaController mIconAreaController;
Lucas Dupinb46d0a22019-01-11 16:57:16 -0800445 private float mHorizontalPanelTranslation;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400446 private final NotificationLockscreenUserManager mLockscreenUserManager =
447 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monk297c04e2018-08-23 17:16:59 -0400448 protected final NotificationGutsManager mGutsManager =
449 Dependency.get(NotificationGutsManager.class);
Jason Monke59dc402018-08-16 12:05:01 -0400450 private final Rect mTmpRect = new Rect();
451 private final NotificationEntryManager mEntryManager =
452 Dependency.get(NotificationEntryManager.class);
453 private final IStatusBarService mBarService = IStatusBarService.Stub.asInterface(
454 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Will Brockmane718d582019-01-17 16:38:38 -0500455 @VisibleForTesting
456 protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Jason Monke59dc402018-08-16 12:05:01 -0400457 private final NotificationRemoteInputManager mRemoteInputManager =
458 Dependency.get(NotificationRemoteInputManager.class);
459 private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
460
461 private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
462 private final LockscreenGestureLogger mLockscreenGestureLogger =
463 Dependency.get(LockscreenGestureLogger.class);
464 private final VisualStabilityManager mVisualStabilityManager =
465 Dependency.get(VisualStabilityManager.class);
466 protected boolean mClearAllEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +0100467
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200468 private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
Jason Monke59dc402018-08-16 12:05:01 -0400469 private NotificationPanelView mNotificationPanel;
Jason Monk297c04e2018-08-23 17:16:59 -0400470 private final ShadeController mShadeController = Dependency.get(ShadeController.class);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400471
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400472 private final NotificationGutsManager
473 mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800474 /**
475 * If the {@link NotificationShelf} should be visible when dark.
476 */
477 private boolean mShowDarkShelf;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400478
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500479 @Inject
480 public NotificationStackScrollLayout(
481 @Named(VIEW_CONTEXT) Context context,
482 AttributeSet attrs,
Lucas Dupin00be88f2019-01-03 17:50:52 -0800483 @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
484 NotificationRoundnessManager notificationRoundnessManager) {
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500485 super(context, attrs, 0, 0);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800486 Resources res = getResources();
487
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500488 mAllowLongPress = allowLongPress;
489
Gus Prevase2d6f042018-10-17 15:25:30 -0400490 for (int i = 0; i < NUM_SECTIONS; i++) {
491 mSections[i] = new NotificationSection(this);
492 }
493
Selim Cinek281c2022016-10-13 19:14:43 -0700494 mAmbientState = new AmbientState(context);
Lucas Dupin00be88f2019-01-03 17:50:52 -0800495 mRoundnessManager = notificationRoundnessManager;
Selim Cinekd35c2792016-01-21 13:20:57 -0800496 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800497 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
498 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400499 mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
Selim Cinek1cf41c12014-08-12 20:06:19 +0200500 minHeight, maxHeight);
501 mExpandHelper.setEventSource(this);
502 mExpandHelper.setScrollAdapter(this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400503 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
504 getContext(), mMenuEventListener);
Muyuan Li333a4fc2016-04-16 17:13:46 -0700505 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100506 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800507 mFalsingManager = FalsingManager.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800508 mShouldDrawNotificationBackground =
509 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700510 mFadeNotificationsOnDismiss =
511 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Selim Cinek29aab962018-02-27 17:05:45 -0800512 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
513 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800514 addOnExpandedHeightListener(mRoundnessManager::setExpanded);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800515
Rohan Shah524cf7b2018-03-15 14:40:02 -0700516 // Blocking helper manager wants to know the expanded state, update as well.
517 NotificationBlockingHelperManager blockingHelperManager =
518 Dependency.get(NotificationBlockingHelperManager.class);
519 addOnExpandedHeightListener((height, unused) -> {
520 blockingHelperManager.setNotificationShadeExpanded(height);
521 });
522
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800523 updateWillNotDraw();
Lucas Dupind285cf02018-01-18 09:18:23 -0800524 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100525 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100526 mDebugPaint = new Paint();
527 mDebugPaint.setColor(0xffff0000);
528 mDebugPaint.setStrokeWidth(2);
529 mDebugPaint.setStyle(Paint.Style.STROKE);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500530 mDebugPaint.setTextSize(25f);
Selim Cinek67b22602014-03-10 15:40:16 +0100531 }
Jason Monke59dc402018-08-16 12:05:01 -0400532 mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
Gus Prevas33619af2018-10-26 15:40:27 -0400533
534 TunerService tunerService = Dependency.get(TunerService.class);
535 tunerService.addTunable((key, newValue) -> {
536 if (key.equals(LOW_PRIORITY)) {
537 mLowPriorityBeforeSpeedBump = "1".equals(newValue);
Gus Prevasa18dc572019-01-14 16:11:22 -0500538 } else if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) {
539 updateDismissRtlSetting("1".equals(newValue));
Gus Prevas33619af2018-10-26 15:40:27 -0400540 }
Gus Prevasa18dc572019-01-14 16:11:22 -0500541 }, LOW_PRIORITY, Settings.Secure.NOTIFICATION_DISMISS_RTL);
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500542
543 mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
544 @Override
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800545 public void onPostEntryUpdated(NotificationEntry entry) {
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500546 if (!entry.notification.isClearable()) {
547 // The user may have performed a dismiss action on the notification, since it's
548 // not clearable we should snap it back.
549 snapViewIfNeeded(entry);
550 }
551 }
552 });
Jason Monke59dc402018-08-16 12:05:01 -0400553 }
554
Gus Prevasa18dc572019-01-14 16:11:22 -0500555 private void updateDismissRtlSetting(boolean dismissRtl) {
556 mDismissRtl = dismissRtl;
557 for (int i = 0; i < getChildCount(); i++) {
558 View child = getChildAt(i);
559 if (child instanceof ExpandableNotificationRow) {
560 ((ExpandableNotificationRow) child).setDismissRtl(dismissRtl);
561 }
562 }
563 }
564
Jason Monke59dc402018-08-16 12:05:01 -0400565 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400566 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400567 protected void onFinishInflate() {
568 super.onFinishInflate();
569
570 inflateEmptyShadeView();
571 inflateFooterView();
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400572 mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500573 if (mAllowLongPress) {
574 setLongPressListener(mGutsManager::openGuts);
575 }
Jason Monke59dc402018-08-16 12:05:01 -0400576 }
577
578 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400579 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400580 public void onDensityOrFontScaleChanged() {
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800581 reinflateViews();
582 }
583
584 private void reinflateViews() {
Jason Monke59dc402018-08-16 12:05:01 -0400585 inflateFooterView();
586 inflateEmptyShadeView();
587 updateFooter();
588 }
589
590 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400591 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400592 public void onThemeChanged() {
593 int which;
594 if (mStatusBarState == StatusBarState.KEYGUARD
595 || mStatusBarState == StatusBarState.SHADE_LOCKED) {
596 which = WallpaperManager.FLAG_LOCK;
597 } else {
598 which = WallpaperManager.FLAG_SYSTEM;
599 }
600 final boolean useDarkText = mColorExtractor.getColors(which,
601 true /* ignoreVisibility */).supportsDarkText();
602 updateDecorViews(useDarkText);
603
604 updateFooter();
605 }
606
Fabian Kozynskid254b192019-02-05 13:42:58 -0500607 @Override
608 public void onOverlayChanged() {
609 int newRadius = mContext.getResources().getDimensionPixelSize(
610 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
611 if (mCornerRadius != newRadius) {
612 mCornerRadius = newRadius;
613 invalidate();
614 }
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800615 reinflateViews();
Fabian Kozynskid254b192019-02-05 13:42:58 -0500616 }
617
Jason Monke59dc402018-08-16 12:05:01 -0400618 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400619 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400620 public void updateFooter() {
621 boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications();
622 boolean showFooterView = (showDismissView ||
623 mEntryManager.getNotificationData().getActiveNotifications().size() != 0)
624 && mStatusBarState != StatusBarState.KEYGUARD
625 && !mRemoteInputManager.getController().isRemoteInputActive();
626
627 updateFooterView(showFooterView, showDismissView);
628 }
629
630 /**
631 * Return whether there are any clearable notifications
632 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400633 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400634 public boolean hasActiveClearableNotifications() {
635 int childCount = getChildCount();
636 for (int i = 0; i < childCount; i++) {
637 View child = getChildAt(i);
638 if (!(child instanceof ExpandableNotificationRow)) {
639 continue;
640 }
641 if (((ExpandableNotificationRow) child).canViewBeDismissed()) {
642 return true;
643 }
644 }
645 return false;
646 }
647
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400648 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400649 public RemoteInputController.Delegate createDelegate() {
Jason Monke59dc402018-08-16 12:05:01 -0400650 return new RemoteInputController.Delegate() {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500651 public void setRemoteInputActive(NotificationEntry entry,
Jason Monke59dc402018-08-16 12:05:01 -0400652 boolean remoteInputActive) {
653 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
Evan Laird94492852018-10-25 13:43:01 -0400654 entry.notifyHeightChanged(true /* needsAnimation */);
Jason Monke59dc402018-08-16 12:05:01 -0400655 updateFooter();
656 }
657
Ned Burnsf81c4c42019-01-07 14:10:43 -0500658 public void lockScrollTo(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400659 NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
Jason Monke59dc402018-08-16 12:05:01 -0400660 }
661
662 public void requestDisallowLongPressAndDismiss() {
663 requestDisallowLongPress();
664 requestDisallowDismiss();
665 }
666 };
Selim Cinek67b22602014-03-10 15:40:16 +0100667 }
668
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900669 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400670 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400671 protected void onAttachedToWindow() {
672 super.onAttachedToWindow();
Beverly8fdb5332019-02-04 14:29:49 -0500673 ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class))
674 .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
Jason Monke59dc402018-08-16 12:05:01 -0400675 Dependency.get(ConfigurationController.class).addCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400676 }
677
678 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400679 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400680 protected void onDetachedFromWindow() {
681 super.onDetachedFromWindow();
Jason Monkaf08c152018-12-04 11:12:39 -0500682 Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Jason Monke59dc402018-08-16 12:05:01 -0400683 Dependency.get(ConfigurationController.class).removeCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400684 }
685
686 @Override
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400687 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -0800688 public NotificationSwipeActionHelper getSwipeActionHelper() {
689 return mSwipeHelper;
690 }
691
Selim Cinek67b22602014-03-10 15:40:16 +0100692 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400693 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupinf03e7522018-06-25 16:21:13 -0700694 public void onUiModeChanged() {
695 mBgColor = mContext.getColor(R.color.notification_shade_background_color);
696 updateBackgroundDimming();
Selim Cinekab9c7b22018-12-11 18:15:47 -0800697 mShelf.onUiModeChanged();
Lucas Dupinf03e7522018-06-25 16:21:13 -0700698 }
699
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400700 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Selim Cinek67b22602014-03-10 15:40:16 +0100701 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800702 if (mShouldDrawNotificationBackground
Gus Prevase2d6f042018-10-17 15:25:30 -0400703 && (mSections[0].getCurrentBounds().top
704 < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
705 || mAmbientState.isDark())) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800706 drawBackground(canvas);
Gus Prevas211181532018-12-13 14:49:33 -0500707 } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
708 drawHeadsUpBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700709 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800710
Selim Cinek67b22602014-03-10 15:40:16 +0100711 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800712 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100713 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800714 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100715 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200716 y = getHeight() - getEmptyBottomMargin();
717 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100718 }
719 }
720
Gus Prevas0fa58d62019-01-11 13:58:40 -0500721 @Override
722 public void draw(Canvas canvas) {
723 super.draw(canvas);
724
725 if (DEBUG && ANCHOR_SCROLLING) {
726 if (mScrollAnchorView instanceof ExpandableNotificationRow) {
727 canvas.drawRect(0,
728 mScrollAnchorView.getTranslationY(),
729 getWidth(),
730 mScrollAnchorView.getTranslationY()
731 + ((ExpandableNotificationRow) mScrollAnchorView).getActualHeight(),
732 mDebugPaint);
733 canvas.drawText(Integer.toString(mScrollAnchorViewY), getWidth() - 200,
734 mScrollAnchorView.getTranslationY() + 30, mDebugPaint);
735 int y = (int) mShelf.getTranslationY();
736 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
737 }
Gus Prevascdc98342019-01-14 14:29:44 -0500738 canvas.drawText(Integer.toString(getMaxNegativeScrollAmount()), getWidth() - 100,
739 getIntrinsicPadding() + 30, mDebugPaint);
740 canvas.drawText(Integer.toString(getMaxPositiveScrollAmount()), getWidth() - 100,
741 getHeight() - 30, mDebugPaint);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500742 }
743 }
744
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400745 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupind285cf02018-01-18 09:18:23 -0800746 private void drawBackground(Canvas canvas) {
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700747 int lockScreenLeft = mSidePaddings;
748 int lockScreenRight = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -0400749 int lockScreenTop = mSections[0].getCurrentBounds().top;
750 int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom;
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700751 int darkLeft = getWidth() / 2;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800752 int darkTop = mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800753
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700754 float yProgress = 1 - mInterpolatedDarkAmount;
755 float xProgress = mDarkXInterpolator.getInterpolation(
756 (1 - mLinearDarkAmount) * mBackgroundXFactor);
Lucas Dupin60661a62018-04-12 10:50:13 -0700757
Gus Prevase2d6f042018-10-17 15:25:30 -0400758 int left = (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress);
759 int right = (int) MathUtils.lerp(darkLeft, lockScreenRight, xProgress);
760 int top = (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress);
761 int bottom = (int) MathUtils.lerp(darkTop, lockScreenBottom, yProgress);
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700762 mBackgroundAnimationRect.set(
Gus Prevase2d6f042018-10-17 15:25:30 -0400763 left,
764 top,
765 right,
766 bottom);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200767
Gus Prevase2d6f042018-10-17 15:25:30 -0400768 int backgroundTopAnimationOffset = top - lockScreenTop;
769 // TODO(kprevas): this may not be necessary any more since we don't display the shelf in AOD
770 boolean anySectionHasVisibleChild = false;
771 for (NotificationSection section : mSections) {
772 if (section.getFirstVisibleChild() != null) {
773 anySectionHasVisibleChild = true;
774 break;
775 }
776 }
777 if (!mAmbientState.isDark() || anySectionHasVisibleChild) {
778 drawBackgroundRects(canvas, left, right, top, backgroundTopAnimationOffset);
Lucas Dupind285cf02018-01-18 09:18:23 -0800779 }
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700780
Lucas Dupin16cfe452018-02-08 13:14:50 -0800781 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800782 }
783
Gus Prevase2d6f042018-10-17 15:25:30 -0400784 /**
785 * Draws round rects for each background section.
786 *
787 * We want to draw a round rect for each background section as defined by {@link #mSections}.
788 * However, if two sections are directly adjacent with no gap between them (e.g. on the
789 * lockscreen where the shelf can appear directly below the high priority section, or while
790 * scrolling the shade so that the top of the shelf is right at the bottom of the high priority
791 * section), we don't want to round the adjacent corners.
792 *
793 * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we
794 * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect.
795 * This method tracks the top of each rect we need to draw, then iterates through the visible
796 * sections. If a section is not adjacent to the previous section, we draw the previous rect
797 * behind the sections we've accumulated up to that point, then start a new rect at the top of
798 * the current section. When we're done iterating we will always have one rect left to draw.
799 */
800 private void drawBackgroundRects(Canvas canvas, int left, int right, int top,
801 int animationYOffset) {
802 int backgroundRectTop = top;
803 int lastSectionBottom =
804 mSections[0].getCurrentBounds().bottom + animationYOffset;
805 for (NotificationSection section : mSections) {
806 if (section.getFirstVisibleChild() == null) {
807 continue;
808 }
809 int sectionTop = section.getCurrentBounds().top + animationYOffset;
810 // If sections are directly adjacent to each other, we don't want to draw them
811 // as separate roundrects, as the rounded corners right next to each other look
812 // bad.
813 if (sectionTop - lastSectionBottom > DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX) {
814 canvas.drawRoundRect(left,
815 backgroundRectTop,
816 right,
817 lastSectionBottom,
818 mCornerRadius, mCornerRadius, mBackgroundPaint);
819 backgroundRectTop = sectionTop;
820 }
821 lastSectionBottom =
822 section.getCurrentBounds().bottom + animationYOffset;
823 }
824 canvas.drawRoundRect(left,
825 backgroundRectTop,
826 right,
827 lastSectionBottom,
828 mCornerRadius, mCornerRadius, mBackgroundPaint);
829 }
830
Gus Prevas211181532018-12-13 14:49:33 -0500831 private void drawHeadsUpBackground(Canvas canvas) {
832 int left = mSidePaddings;
833 int right = getWidth() - mSidePaddings;
834
835 float top = getHeight();
836 float bottom = 0;
837 int childCount = getChildCount();
838 for (int i = 0; i < childCount; i++) {
839 View child = getChildAt(i);
840 if (child.getVisibility() != View.GONE
841 && child instanceof ExpandableNotificationRow) {
842 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
843 if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0) {
844 top = Math.min(top, row.getTranslationY());
845 bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
846 }
847 }
848 }
849
850 if (top < bottom) {
851 canvas.drawRoundRect(
852 left, top, right, bottom,
853 mCornerRadius, mCornerRadius, mBackgroundPaint);
854 }
855 }
856
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400857 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd35c2792016-01-21 13:20:57 -0800858 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800859 // No need to update the background color if it's not being drawn.
860 if (!mShouldDrawNotificationBackground) {
861 return;
862 }
863
Lucas Dupinb561eda2018-04-09 17:25:04 -0700864 float alpha =
865 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200866 alpha *= 1f - mInterpolatedDarkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -0700867 // We need to manually blend in the background color.
868 int scrimColor = mScrimController.getBackgroundColor();
869 int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
870
871 // Interpolate between semi-transparent notification panel background color
872 // and white AOD separator.
Lucas Dupinf03e7522018-06-25 16:21:13 -0700873 float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */,
874 mLinearDarkAmount);
Lucas Dupinb561eda2018-04-09 17:25:04 -0700875 int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800876
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100877 if (mCachedBackgroundColor != color) {
878 mCachedBackgroundColor = color;
879 mBackgroundPaint.setColor(color);
880 invalidate();
881 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800882 }
883
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400884 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +0100885 private void initView(Context context) {
886 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100887 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200888 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100889 final ViewConfiguration configuration = ViewConfiguration.get(context);
890 mTouchSlop = configuration.getScaledTouchSlop();
891 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
892 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100893 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700894
895 Resources res = context.getResources();
896 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800897 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700898 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700899 mPaddingBetweenElements = Math.max(1,
900 res.getDimensionPixelSize(R.dimen.notification_divider_height));
901 mIncreasedPaddingBetweenElements =
902 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
903 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200904 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800905 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700906 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800907 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700908 mMinInteractionHeight = res.getDimensionPixelSize(
909 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800910 mCornerRadius = res.getDimensionPixelSize(
911 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800912 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
913 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200914 }
915
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400916 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekaef92ef2014-06-06 18:06:04 +0200917 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700918 notifyHeightChangeListener(view, false /* needsAnimation */);
919 }
920
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400921 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -0700922 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +0200923 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700924 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200925 }
Selim Cinek67b22602014-03-10 15:40:16 +0100926 }
927
928 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400929 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +0100930 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
931 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -0800932
933 int width = MeasureSpec.getSize(widthMeasureSpec);
934 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
935 MeasureSpec.getMode(widthMeasureSpec));
Evan Lairdeb7dbd52018-06-28 13:17:25 -0400936 // Don't constrain the height of the children so we know how big they'd like to be
937 int childHeightSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
938 MeasureSpec.UNSPECIFIED);
939
Selim Cinekfa760d42016-05-10 15:50:53 -0400940 // We need to measure all children even the GONE ones, such that the heights are calculated
941 // correctly as they are used to calculate how many we can fit on the screen.
942 final int size = getChildCount();
943 for (int i = 0; i < size; i++) {
Evan Lairdeb7dbd52018-06-28 13:17:25 -0400944 measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400945 }
Selim Cinek67b22602014-03-10 15:40:16 +0100946 }
947
948 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400949 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +0100950 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +0100951 // we layout all our children centered on the top
952 float centerX = getWidth() / 2.0f;
953 for (int i = 0; i < getChildCount(); i++) {
954 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400955 // We need to layout all children even the GONE ones, such that the heights are
956 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +0100957 float width = child.getMeasuredWidth();
958 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100959 child.layout((int) (centerX - width / 2.0f),
960 0,
961 (int) (centerX + width / 2.0f),
962 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +0100963 }
Jorim Jaggi1d480692014-05-20 19:41:58 +0200964 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +0100965 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +0200966 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +0200967 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -0800968 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -0700969 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100970 }
971
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400972 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5bc852a2015-12-21 12:19:09 -0800973 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
974 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +0200975 mNeedViewResizeAnimation = true;
976 mNeedsAnimation = true;
977 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200978 }
979
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400980 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekdb167372016-11-17 15:41:17 -0800981 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
982 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -0800983 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +0200984 }
985
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900986 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400987 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900988 public void setChildLocationsChangedListener(
989 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200990 mListener = listener;
991 }
992
Selim Cineka7d4f822016-12-06 14:34:47 -0800993 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400994 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Ned Burnsf81c4c42019-01-07 14:10:43 -0500995 public boolean isInVisibleLocation(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400996 ExpandableNotificationRow row = entry.getRow();
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500997 ExpandableViewState childViewState = row.getViewState();
998
Christoph Studer6e3eceb2014-04-01 18:40:27 +0200999 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001000 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001001 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +01001002 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001003 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +01001004 }
Selim Cineka7d4f822016-12-06 14:34:47 -08001005 if (row.getVisibility() != View.VISIBLE) {
1006 return false;
1007 }
1008 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001009 }
1010
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001011 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinek67b22602014-03-10 15:40:16 +01001012 private void setMaxLayoutHeight(int maxLayoutHeight) {
1013 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -07001014 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001015 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +01001016 }
1017
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001018 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001019 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001020 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -07001021 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001022 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +01001023 }
1024
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001025 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinekbc243a92016-09-27 16:35:13 -07001026 private void updateAlgorithmLayoutMinHeight() {
shawnlinc3457912018-05-15 16:39:56 +08001027 mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001028 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -07001029 }
1030
Selim Cinek67b22602014-03-10 15:40:16 +01001031 /**
1032 * Updates the children views according to the stack scroll algorithm. Call this whenever
1033 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
1034 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001035 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001036 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -08001037 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -08001038 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
1039 ? 0
1040 : mScroller.getCurrVelocity());
Gus Prevas0fa58d62019-01-11 13:58:40 -05001041 if (ANCHOR_SCROLLING) {
1042 mAmbientState.setAnchorViewIndex(indexOfChild(mScrollAnchorView));
1043 mAmbientState.setAnchorViewY(mScrollAnchorViewY);
1044 } else {
1045 mAmbientState.setScrollY(mOwnScrollY);
1046 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001047 mStackScrollAlgorithm.resetViewStates(mAmbientState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001048 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02001049 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +01001050 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +02001051 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +01001052 }
1053 }
1054
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001055 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc383fd02016-10-21 15:31:26 -07001056 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -07001057 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001058 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -07001059 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
1060 updateBackground();
1061 }
Selim Cinekc383fd02016-10-21 15:31:26 -07001062 }
1063
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001064 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001065 private void updateClippingToTopRoundedCorner() {
Arthur Hungc0ef5652018-05-22 14:00:42 +08001066 Float clipStart = (float) mTopPadding
Jason Monke59dc402018-08-16 12:05:01 -04001067 + mStackTranslation
1068 + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001069 Float clipEnd = clipStart + mCornerRadius;
1070 boolean first = true;
1071 for (int i = 0; i < getChildCount(); i++) {
1072 ExpandableView child = (ExpandableView) getChildAt(i);
1073 if (child.getVisibility() == GONE) {
1074 continue;
1075 }
1076 float start = child.getTranslationY();
Arthur Hungc0ef5652018-05-22 14:00:42 +08001077 float end = start + child.getActualHeight();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001078 boolean clip = clipStart > start && clipStart < end
1079 || clipEnd >= start && clipEnd <= end;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001080 clip &= !(first && isScrolledToTop());
Selim Cinekeccf4942018-05-30 09:55:36 -07001081 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
1082 : ExpandableView.NO_ROUNDNESS);
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001083 first = false;
1084 }
1085 }
1086
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001087 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek3776fe02016-02-04 13:32:43 -08001088 private void updateScrollStateForAddedChildren() {
1089 if (mChildrenToAddAnimated.isEmpty()) {
1090 return;
1091 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001092 if (!ANCHOR_SCROLLING) {
1093 for (int i = 0; i < getChildCount(); i++) {
1094 ExpandableView child = (ExpandableView) getChildAt(i);
1095 if (mChildrenToAddAnimated.contains(child)) {
1096 int startingPosition = getPositionInLinearLayout(child);
1097 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
1098 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
1099 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
1100 int childHeight = getIntrinsicHeight(child) + padding;
1101 if (startingPosition < mOwnScrollY) {
1102 // This child starts off screen, so let's keep it offscreen to keep the
1103 // others visible
Selim Cinek3776fe02016-02-04 13:32:43 -08001104
Gus Prevas0fa58d62019-01-11 13:58:40 -05001105 setOwnScrollY(mOwnScrollY + childHeight);
1106 }
Selim Cinek3776fe02016-02-04 13:32:43 -08001107 }
1108 }
1109 }
1110 clampScrollPosition();
1111 }
1112
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001113 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001114 private void updateForcedScroll() {
1115 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
1116 || !mForcedScroll.isAttachedToWindow())) {
1117 mForcedScroll = null;
1118 }
1119 if (mForcedScroll != null) {
1120 ExpandableView expandableView = (ExpandableView) mForcedScroll;
1121 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
1122 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -07001123 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -04001124
Gus Prevas0fa58d62019-01-11 13:58:40 -05001125 if (ANCHOR_SCROLLING) {
1126 // TODO
1127 } else {
1128 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -07001129
Gus Prevas0fa58d62019-01-11 13:58:40 -05001130 // Only apply the scroll if we're scrolling the view upwards, or the view is so
1131 // far up that it is not visible anymore.
1132 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1133 setOwnScrollY(targetScroll);
1134 }
Adrian Roos181385c2016-05-05 17:45:44 -04001135 }
1136 }
1137 }
1138
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001139 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek319bdc42014-05-01 23:01:58 +02001140 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +02001141 if (!mChildrenUpdateRequested) {
1142 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
1143 mChildrenUpdateRequested = true;
1144 invalidate();
1145 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001146 }
1147
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001148 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001149 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +02001150 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +01001151 }
1152
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001153 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekf7a14c02014-07-07 14:01:46 +02001154 private void clampScrollPosition() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001155 if (ANCHOR_SCROLLING) {
1156 // TODO
1157 } else {
1158 int scrollRange = getScrollRange();
1159 if (scrollRange < mOwnScrollY) {
1160 setOwnScrollY(scrollRange);
1161 }
Selim Cinek67b22602014-03-10 15:40:16 +01001162 }
1163 }
1164
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001165 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001166 public int getTopPadding() {
1167 return mTopPadding;
1168 }
1169
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001170 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001171 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupin00be88f2019-01-03 17:50:52 -08001172 if (mTopPadding != topPadding) {
1173 mTopPadding = topPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001174 updateAlgorithmHeightAndPadding();
1175 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +02001176 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001177 mTopPaddingNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04001178 mNeedsAnimation = true;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001179 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001180 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -07001181 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001182 }
1183 }
1184
1185 /**
Selim Cinekbc243a92016-09-27 16:35:13 -07001186 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001187 *
Selim Cinekbc243a92016-09-27 16:35:13 -07001188 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001189 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001190 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbc243a92016-09-27 16:35:13 -07001191 public void setExpandedHeight(float height) {
1192 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001193 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001194 int minExpansionHeight = getMinExpansionHeight();
1195 if (height < minExpansionHeight) {
1196 mClipRect.left = 0;
1197 mClipRect.right = getWidth();
1198 mClipRect.top = 0;
1199 mClipRect.bottom = (int) height;
1200 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001201 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001202 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001203 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001204 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001205 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -07001206 float translationY;
1207 float appearEndPosition = getAppearEndPosition();
1208 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001209 float appearFraction = 1.0f;
shawnlin5be1f7c2018-05-21 20:50:54 +08001210 boolean appearing = height < appearEndPosition;
1211 mAmbientState.setAppearing(appearing);
1212 if (!appearing) {
Selim Cinekbc243a92016-09-27 16:35:13 -07001213 translationY = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +08001214 if (mShouldShowShelfOnly) {
1215 stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
1216 } else if (mQsExpanded) {
1217 int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
1218 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
1219 if (stackStartPosition <= stackEndPosition) {
1220 stackHeight = stackEndPosition;
1221 } else {
1222 stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
1223 stackEndPosition, mQsExpansionFraction);
1224 }
1225 } else {
1226 stackHeight = (int) height;
1227 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001228 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001229 appearFraction = getAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -07001230 if (appearFraction >= 0) {
1231 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
1232 appearFraction);
1233 } else {
1234 // This may happen when pushing up a heads up. We linearly push it up from the
1235 // start
1236 translationY = height - appearStartPosition + getExpandTranslationStart();
1237 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001238 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001239 stackHeight =
1240 getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001241 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
1242 } else {
1243 stackHeight = (int) (height - translationY);
1244 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001245 }
1246 if (stackHeight != mCurrentStackHeight) {
1247 mCurrentStackHeight = stackHeight;
1248 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +02001249 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001250 }
Selim Cinek94c2d822016-07-13 18:50:04 -07001251 setStackTranslation(translationY);
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001252 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
1253 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
1254 listener.accept(mExpandedHeight, appearFraction);
1255 }
Selim Cinekcafa87f2016-10-26 17:00:17 -07001256 }
1257
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001258 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001259 private void setRequestedClipBounds(Rect clipRect) {
1260 mRequestedClipBounds = clipRect;
1261 updateClipping();
1262 }
1263
Lucas Dupin60661a62018-04-12 10:50:13 -07001264 /**
1265 * Return the height of the content ignoring the footer.
1266 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001267 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -07001268 public int getIntrinsicContentHeight() {
1269 return mIntrinsicContentHeight;
1270 }
1271
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001272 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001273 public void updateClipping() {
1274 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
1275 && !mHeadsUpAnimatingAway;
1276 if (mIsClipped != clipped) {
1277 mIsClipped = clipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001278 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08001279
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08001280 if (mPulsing || mAmbientState.isFullyDark() && mShowDarkShelf) {
Lucas Dupin4798ea12018-11-05 19:26:24 -08001281 setClipBounds(null);
1282 } else if (mAmbientState.isDarkAtAll()) {
Lucas Dupin16cfe452018-02-08 13:14:50 -08001283 setClipBounds(mBackgroundAnimationRect);
1284 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001285 setClipBounds(mRequestedClipBounds);
1286 } else {
1287 setClipBounds(null);
1288 }
Selim Cinek94c2d822016-07-13 18:50:04 -07001289 }
1290
1291 /**
1292 * @return The translation at the beginning when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001293 * Measured relative to the resting position.
Selim Cinek94c2d822016-07-13 18:50:04 -07001294 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001295 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001296 private float getExpandTranslationStart() {
Selim Cinek083f2142018-11-06 16:32:23 -08001297 return -mTopPadding + getMinExpansionHeight() - mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001298 }
1299
1300 /**
1301 * @return the position from where the appear transition starts when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001302 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001303 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001304 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001305 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001306 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001307 return mHeadsUpInset
1308 + getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -07001309 }
Selim Cinek48ff9b42016-11-09 19:31:51 -08001310 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001311 }
1312
1313 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001314 * @return the height of the top heads up notification when pinned. This is different from the
Jason Monke59dc402018-08-16 12:05:01 -04001315 * intrinsic height, which also includes whether the notification is system expanded and
1316 * is mainly used when dragging down from a heads up notification.
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001317 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001318 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001319 private int getTopHeadsUpPinnedHeight() {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001320 NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001321 if (topEntry == null) {
1322 return 0;
1323 }
Evan Laird94492852018-10-25 13:43:01 -04001324 ExpandableNotificationRow row = topEntry.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001325 if (row.isChildInGroup()) {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001326 final NotificationEntry groupSummary
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001327 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
1328 if (groupSummary != null) {
Evan Laird94492852018-10-25 13:43:01 -04001329 row = groupSummary.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001330 }
1331 }
1332 return row.getPinnedHeadsUpHeight();
1333 }
1334
1335 /**
Selim Cinek94c2d822016-07-13 18:50:04 -07001336 * @return the position from where the appear transition ends when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001337 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001338 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001339 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001340 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -07001341 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -07001342 int notGoneChildCount = getNotGoneChildCount();
Julia Reynolds34f14962018-05-03 12:40:20 +00001343 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001344 if (isHeadsUpTransition()
Selim Cinekebf42342017-07-13 15:46:10 +02001345 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001346 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +01001347 } else {
1348 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001349 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1350 appearPosition += mShelf.getIntrinsicHeight();
1351 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001352 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001353 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001354 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001355 }
1356 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001357 }
1358
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001359 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001360 private boolean isHeadsUpTransition() {
Gus Prevase2d6f042018-10-17 15:25:30 -04001361 NotificationSection firstVisibleSection = getFirstVisibleSection();
1362 return mTrackingHeadsUp && firstVisibleSection != null
1363 && mAmbientState.isAboveShelf(firstVisibleSection.getFirstVisibleChild());
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001364 }
1365
Selim Cinek94c2d822016-07-13 18:50:04 -07001366 /**
1367 * @param height the height of the panel
1368 * @return the fraction of the appear animation that has been performed
1369 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001370 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001371 public float getAppearFraction(float height) {
1372 float appearEndPosition = getAppearEndPosition();
1373 float appearStartPosition = getAppearStartPosition();
1374 return (height - appearStartPosition)
1375 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001376 }
1377
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001378 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001379 public float getStackTranslation() {
1380 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001381 }
1382
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001383 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001384 private void setStackTranslation(float stackTranslation) {
1385 if (stackTranslation != mStackTranslation) {
1386 mStackTranslation = stackTranslation;
1387 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001388 requestChildrenUpdate();
1389 }
Selim Cinek67b22602014-03-10 15:40:16 +01001390 }
1391
1392 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001393 * 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 +01001394 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1395 *
1396 * @return either the layout height or the externally defined height, whichever is smaller
1397 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001398 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek343e6e22014-04-11 21:23:30 +02001399 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001400 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1401 }
1402
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001403 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek816c8e42015-11-19 12:00:45 -08001404 public int getFirstItemMinHeight() {
1405 final ExpandableView firstChild = getFirstChildNotGone();
1406 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001407 }
1408
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001409 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Jason Monk16ac3772016-02-10 15:39:21 -05001410 public void setQsContainer(ViewGroup qsContainer) {
1411 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001412 }
1413
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001414 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek684a4422015-04-15 16:18:39 -07001415 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001416 if (v instanceof ExpandableNotificationRow) {
1417 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001418 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001419 }
1420 return false;
1421 }
1422
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001423 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cineka59ecc32015-04-07 10:51:49 -07001424 private boolean isHeadsUp(View v) {
1425 if (v instanceof ExpandableNotificationRow) {
1426 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1427 return row.isHeadsUp();
1428 }
1429 return false;
1430 }
1431
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001432 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001433 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1434 getLocationOnScreen(mTempInt2);
1435 float localTouchY = touchY - mTempInt2[1];
1436
1437 ExpandableView closestChild = null;
1438 float minDist = Float.MAX_VALUE;
1439
1440 // find the view closest to the location, accounting for GONE views
1441 final int count = getChildCount();
1442 for (int childIdx = 0; childIdx < count; childIdx++) {
1443 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1444 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001445 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001446 continue;
1447 }
1448 float childTop = slidingChild.getTranslationY();
1449 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001450 float bottom = childTop + slidingChild.getActualHeight()
1451 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001452
1453 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1454 if (dist < minDist) {
1455 closestChild = slidingChild;
1456 minDist = dist;
1457 }
1458 }
1459 return closestChild;
1460 }
1461
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001462 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
1463 private ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001464 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1465
1466 }
1467
1468 /**
1469 * Get the child at a certain screen location.
1470 *
Jason Monke59dc402018-08-16 12:05:01 -04001471 * @param touchX the x coordinate
1472 * @param touchY the y coordinate
Selim Cinek34ed7c02017-09-08 15:03:12 -07001473 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1474 * @return the child at the given location.
1475 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001476 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek34ed7c02017-09-08 15:03:12 -07001477 private ExpandableView getChildAtPosition(float touchX, float touchY,
1478 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001479 // find the view under the pointer, accounting for GONE views
1480 final int count = getChildCount();
1481 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001482 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001483 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001484 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001485 continue;
1486 }
Selim Cinek89faff12014-06-19 16:29:04 -07001487 float childTop = slidingChild.getTranslationY();
1488 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001489 float bottom = childTop + slidingChild.getActualHeight()
1490 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001491
1492 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1493 // camera affordance).
1494 int left = 0;
1495 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001496
Selim Cinek34ed7c02017-09-08 15:03:12 -07001497 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001498 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001499 if (slidingChild instanceof ExpandableNotificationRow) {
1500 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Ned Burnsf81c4c42019-01-07 14:10:43 -05001501 NotificationEntry entry = row.getEntry();
Selim Cinek131c1e22015-05-11 19:04:49 -07001502 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Evan Laird94492852018-10-25 13:43:01 -04001503 && mHeadsUpManager.getTopEntry().getRow() != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001504 && mGroupManager.getGroupSummary(
Evan Laird94492852018-10-25 13:43:01 -04001505 mHeadsUpManager.getTopEntry().notification)
1506 != entry) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001507 continue;
1508 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001509 return row.getViewAtPosition(touchY - childTop);
1510 }
Selim Cinek67b22602014-03-10 15:40:16 +01001511 return slidingChild;
1512 }
1513 }
1514 return null;
1515 }
1516
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001517 private ExpandableView getChildAtRawPosition(float touchX, float touchY) {
1518 getLocationOnScreen(mTempInt2);
1519 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001520 }
1521
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001522 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001523 public void setScrollingEnabled(boolean enable) {
1524 mScrollingEnabled = enable;
1525 }
1526
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001527 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001528 public void lockScrollTo(View v) {
1529 if (mForcedScroll == v) {
1530 return;
1531 }
1532 mForcedScroll = v;
1533 scrollTo(v);
1534 }
1535
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001536 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001537 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001538 ExpandableView expandableView = (ExpandableView) v;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001539 if (ANCHOR_SCROLLING) {
1540 // TODO
1541 } else {
1542 int positionInLinearLayout = getPositionInLinearLayout(v);
1543 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1544 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001545
Gus Prevas0fa58d62019-01-11 13:58:40 -05001546 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1547 // that it is not visible anymore.
1548 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1549 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1550 mDontReportNextOverScroll = true;
1551 animateScroll();
1552 return true;
1553 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001554 }
Ricky Waicd35def2016-05-03 11:07:07 +01001555 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001556 }
1557
Adrian Roos181385c2016-05-05 17:45:44 -04001558 /**
1559 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
Jason Monke59dc402018-08-16 12:05:01 -04001560 * the IME.
Adrian Roos181385c2016-05-05 17:45:44 -04001561 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001562 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos181385c2016-05-05 17:45:44 -04001563 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1564 return positionInLinearLayout + v.getIntrinsicHeight() +
felkachang529bfe62018-07-04 12:51:44 +08001565 getImeInset() - getHeight()
1566 + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
Adrian Roos181385c2016-05-05 17:45:44 -04001567 }
1568
Adrian Roos5153d4a2016-03-22 10:01:56 -07001569 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001570 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001571 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001572 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001573
Gus Prevas0fa58d62019-01-11 13:58:40 -05001574 if (ANCHOR_SCROLLING) {
1575 // TODO
1576 } else {
1577 int range = getScrollRange();
1578 if (mOwnScrollY > range) {
1579 // HACK: We're repeatedly getting staggered insets here while the IME is
1580 // animating away. To work around that we'll wait until things have settled.
1581 removeCallbacks(mReclamp);
1582 postDelayed(mReclamp, 50);
1583 } else if (mForcedScroll != null) {
1584 // The scroll was requested before we got the actual inset - in case we need
1585 // to scroll up some more do so now.
1586 scrollTo(mForcedScroll);
1587 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001588 }
1589 return insets;
1590 }
1591
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001592 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001593 private Runnable mReclamp = new Runnable() {
1594 @Override
1595 public void run() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001596 if (ANCHOR_SCROLLING) {
1597 // TODO
1598 } else {
1599 int range = getScrollRange();
1600 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1601 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001602 mDontReportNextOverScroll = true;
1603 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001604 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001605 }
1606 };
1607
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001608 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
1609 public void setExpandingEnabled(boolean enable) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001610 mExpandHelper.setEnabled(enable);
1611 }
1612
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001613 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001614 private boolean isScrollingEnabled() {
1615 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001616 }
1617
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001618 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04001619 private boolean canChildBeDismissed(View v) {
Selim Cinek9c17b772015-07-07 20:37:09 -07001620 return StackScrollAlgorithm.canChildBeDismissed(v);
Selim Cinek67b22602014-03-10 15:40:16 +01001621 }
1622
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001623 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07001624 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001625 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001626 }
1627
Selim Cinek67b22602014-03-10 15:40:16 +01001628 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001629 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001630 protected void onConfigurationChanged(Configuration newConfig) {
1631 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001632 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001633 float densityScale = getResources().getDisplayMetrics().density;
1634 mSwipeHelper.setDensityScale(densityScale);
1635 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1636 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1637 initView(getContext());
1638 }
1639
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001640 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dan Sandlereceda3d2014-07-21 15:35:01 -04001641 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001642 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1643 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001644 }
1645
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001646 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001647 private void snapViewIfNeeded(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001648 ExpandableNotificationRow child = entry.getRow();
dongwan0605.kim30637e42016-03-02 17:16:47 +09001649 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001650 // If the child is showing the notification menu snap to that
1651 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
Mady Mellor86889c22016-04-18 16:37:06 -07001652 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
dongwan0605.kim30637e42016-03-02 17:16:47 +09001653 }
1654
Selim Cinek67b22602014-03-10 15:40:16 +01001655 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001656 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001657 public ViewGroup getViewParentForNotification(NotificationEntry entry) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001658 return this;
1659 }
1660
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001661 /**
1662 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1663 *
1664 * @param deltaY The amount to scroll upwards, has to be positive.
1665 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001666 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001667 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001668 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001669 private float overScrollUp(int deltaY, int range) {
1670 deltaY = Math.max(deltaY, 0);
1671 float currentTopAmount = getCurrentOverScrollAmount(true);
1672 float newTopAmount = currentTopAmount - deltaY;
1673 if (currentTopAmount > 0) {
1674 setOverScrollAmount(newTopAmount, true /* onTop */,
1675 false /* animate */);
1676 }
1677 // Top overScroll might not grab all scrolling motion,
1678 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001679 if (ANCHOR_SCROLLING) {
1680 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1681 // TODO: once we're recycling this will need to check the adapter position of the child
1682 ExpandableView lastRow = getLastRowNotGone();
Gus Prevascdc98342019-01-14 14:29:44 -05001683 if (lastRow != null && !lastRow.isInShelf()) {
1684 float distanceToMax = Math.max(0, getMaxPositiveScrollAmount());
1685 if (scrollAmount > distanceToMax) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001686 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1687 // We overScroll on the bottom
1688 setOverScrolledPixels(currentBottomPixels + (scrollAmount - distanceToMax),
1689 false /* onTop */,
1690 false /* animate */);
1691 mScrollAnchorViewY -= distanceToMax;
1692 scrollAmount = 0f;
1693 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001694 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001695 return scrollAmount;
1696 } else {
1697 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1698 float newScrollY = mOwnScrollY + scrollAmount;
1699 if (newScrollY > range) {
1700 if (!mExpandedInThisMotion) {
1701 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1702 // We overScroll on the bottom
1703 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1704 false /* onTop */,
1705 false /* animate */);
1706 }
1707 setOwnScrollY(range);
1708 scrollAmount = 0.0f;
1709 }
1710 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001711 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001712 }
1713
1714 /**
1715 * Perform a scroll downward and adapt the overscroll amounts accordingly
1716 *
1717 * @param deltaY The amount to scroll downwards, has to be negative.
1718 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001719 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001720 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001721 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001722 private float overScrollDown(int deltaY) {
1723 deltaY = Math.min(deltaY, 0);
1724 float currentBottomAmount = getCurrentOverScrollAmount(false);
1725 float newBottomAmount = currentBottomAmount + deltaY;
1726 if (currentBottomAmount > 0) {
1727 setOverScrollAmount(newBottomAmount, false /* onTop */,
1728 false /* animate */);
1729 }
1730 // Bottom overScroll might not grab all scrolling motion,
1731 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001732 if (ANCHOR_SCROLLING) {
1733 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1734 // TODO: once we're recycling this will need to check the adapter position of the child
1735 ExpandableView firstChild = getFirstChildNotGone();
1736 float top = firstChild.getTranslationY();
1737 float distanceToTop = mScrollAnchorView.getTranslationY() - top - mScrollAnchorViewY;
1738 if (distanceToTop < -scrollAmount) {
1739 float currentTopPixels = getCurrentOverScrolledPixels(true);
1740 // We overScroll on the top
1741 setOverScrolledPixels(currentTopPixels + (-scrollAmount - distanceToTop),
1742 true /* onTop */,
1743 false /* animate */);
1744 mScrollAnchorView = firstChild;
1745 mScrollAnchorViewY = 0;
1746 scrollAmount = 0f;
1747 }
1748 return scrollAmount;
1749 } else {
1750 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1751 float newScrollY = mOwnScrollY + scrollAmount;
1752 if (newScrollY < 0) {
1753 float currentTopPixels = getCurrentOverScrolledPixels(true);
1754 // We overScroll on the top
1755 setOverScrolledPixels(currentTopPixels - newScrollY,
1756 true /* onTop */,
1757 false /* animate */);
1758 setOwnScrollY(0);
1759 scrollAmount = 0.0f;
1760 }
1761 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001762 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001763 }
1764
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001765 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001766 private void initVelocityTrackerIfNotExists() {
1767 if (mVelocityTracker == null) {
1768 mVelocityTracker = VelocityTracker.obtain();
1769 }
1770 }
1771
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001772 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001773 private void recycleVelocityTracker() {
1774 if (mVelocityTracker != null) {
1775 mVelocityTracker.recycle();
1776 mVelocityTracker = null;
1777 }
1778 }
1779
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001780 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001781 private void initOrResetVelocityTracker() {
1782 if (mVelocityTracker == null) {
1783 mVelocityTracker = VelocityTracker.obtain();
1784 } else {
1785 mVelocityTracker.clear();
1786 }
1787 }
1788
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001789 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001790 public void setFinishScrollingCallback(Runnable runnable) {
1791 mFinishScrollingCallback = runnable;
1792 }
1793
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001794 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9212de82017-02-06 16:04:28 -08001795 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001796 if (mScroller.computeScrollOffset()) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001797 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001798 int oldY = mLastScrollerY;
1799 int y = mScroller.getCurrY();
1800 int deltaY = y - oldY;
1801 if (deltaY != 0) {
1802 int maxNegativeScrollAmount = getMaxNegativeScrollAmount();
1803 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
1804 if ((maxNegativeScrollAmount < 0 && deltaY < maxNegativeScrollAmount)
1805 || (maxPositiveScrollAmount > 0 && deltaY > maxPositiveScrollAmount)) {
1806 // This frame takes us into overscroll, so set the max overscroll based on
1807 // the current velocity
1808 setMaxOverScrollFromCurrentVelocity();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001809 }
Gus Prevascdc98342019-01-14 14:29:44 -05001810 customOverScrollBy(deltaY, oldY, 0, (int) mMaxOverScroll);
1811 mLastScrollerY = y;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001812 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001813 } else {
1814 int oldY = mOwnScrollY;
1815 int y = mScroller.getCurrY();
Selim Cinek67b22602014-03-10 15:40:16 +01001816
Gus Prevas0fa58d62019-01-11 13:58:40 -05001817 if (oldY != y) {
1818 int range = getScrollRange();
1819 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
Gus Prevascdc98342019-01-14 14:29:44 -05001820 // This frame takes us into overscroll, so set the max overscroll based on
1821 // the current velocity
1822 setMaxOverScrollFromCurrentVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +01001823 }
Selim Cinek67b22602014-03-10 15:40:16 +01001824
Gus Prevas0fa58d62019-01-11 13:58:40 -05001825 if (mDontClampNextScroll) {
1826 range = Math.max(range, oldY);
1827 }
1828 customOverScrollBy(y - oldY, oldY, range,
1829 (int) (mMaxOverScroll));
Adrian Roos5153d4a2016-03-22 10:01:56 -07001830 }
Selim Cinek67b22602014-03-10 15:40:16 +01001831 }
1832
Gus Prevascdc98342019-01-14 14:29:44 -05001833 postOnAnimation(mReflingAndAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001834 } else {
1835 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001836 if (mFinishScrollingCallback != null) {
1837 mFinishScrollingCallback.run();
1838 }
Selim Cinek67b22602014-03-10 15:40:16 +01001839 }
1840 }
1841
Gus Prevascdc98342019-01-14 14:29:44 -05001842 private void setMaxOverScrollFromCurrentVelocity() {
1843 float currVelocity = mScroller.getCurrVelocity();
1844 if (currVelocity >= mMinimumVelocity) {
1845 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
Selim Cinek4195dd02014-05-19 18:16:14 +02001846 }
Gus Prevascdc98342019-01-14 14:29:44 -05001847 }
Selim Cinek4195dd02014-05-19 18:16:14 +02001848
Gus Prevascdc98342019-01-14 14:29:44 -05001849 /**
1850 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
1851 * would cause us to exceed the provided maximum overscroll, springs back instead.
1852 *
1853 * This method performs the determination of whether we're exceeding the overscroll and clamps
1854 * the scroll amount if so. The actual scrolling/overscrolling happens in
1855 * {@link #onCustomOverScrolled(int, boolean)} (absolute scrolling) or
1856 * {@link #onCustomOverScrolledBy(int, boolean)} (anchor scrolling).
1857 *
1858 * @param deltaY The (signed) number of pixels to scroll.
1859 * @param scrollY The current scroll position (absolute scrolling only).
1860 * @param scrollRangeY The maximum allowable scroll position (absolute scrolling only).
1861 * @param maxOverScrollY The current (unsigned) limit on number of pixels to overscroll by.
1862 */
Selim Cinek4195dd02014-05-19 18:16:14 +02001863 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevascdc98342019-01-14 14:29:44 -05001864 private void customOverScrollBy(int deltaY, int scrollY, int scrollRangeY, int maxOverScrollY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001865 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001866 boolean clampedY = false;
1867 if (deltaY < 0) {
1868 int maxScrollAmount = getMaxNegativeScrollAmount();
1869 if (maxScrollAmount > Integer.MIN_VALUE) {
1870 maxScrollAmount -= maxOverScrollY;
1871 if (deltaY < maxScrollAmount) {
1872 deltaY = maxScrollAmount;
1873 clampedY = true;
1874 }
1875 }
1876 } else {
1877 int maxScrollAmount = getMaxPositiveScrollAmount();
1878 if (maxScrollAmount < Integer.MAX_VALUE) {
1879 maxScrollAmount += maxOverScrollY;
1880 if (deltaY > maxScrollAmount) {
1881 deltaY = maxScrollAmount;
1882 clampedY = true;
1883 }
1884 }
1885 }
1886 onCustomOverScrolledBy(deltaY, clampedY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05001887 } else {
1888 int newScrollY = scrollY + deltaY;
1889 final int top = -maxOverScrollY;
1890 final int bottom = maxOverScrollY + scrollRangeY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001891
Gus Prevas0fa58d62019-01-11 13:58:40 -05001892 boolean clampedY = false;
1893 if (newScrollY > bottom) {
1894 newScrollY = bottom;
1895 clampedY = true;
1896 } else if (newScrollY < top) {
1897 newScrollY = top;
1898 clampedY = true;
1899 }
Selim Cinek4195dd02014-05-19 18:16:14 +02001900
Gus Prevas0fa58d62019-01-11 13:58:40 -05001901 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001902 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001903 }
1904
1905 /**
1906 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1907 * overscroll effect based on numPixels. By default this will also cancel animations on the
1908 * same overScroll edge.
1909 *
1910 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
1911 * the rubber-banding logic.
Jason Monke59dc402018-08-16 12:05:01 -04001912 * @param onTop Should the effect be applied on top of the scroller.
1913 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001914 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001915 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001916 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001917 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001918 }
1919
1920 /**
1921 * Set the effective overScroll amount which will be directly reflected in the layout.
1922 * By default this will also cancel animations on the same overScroll edge.
1923 *
Jason Monke59dc402018-08-16 12:05:01 -04001924 * @param amount The amount to overScroll by.
1925 * @param onTop Should the effect be applied on top of the scroller.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001926 * @param animate Should an animation be performed.
1927 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001928
1929 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001930 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
1931 setOverScrollAmount(amount, onTop, animate, true);
1932 }
1933
1934 /**
1935 * Set the effective overScroll amount which will be directly reflected in the layout.
1936 *
Jason Monke59dc402018-08-16 12:05:01 -04001937 * @param amount The amount to overScroll by.
1938 * @param onTop Should the effect be applied on top of the scroller.
1939 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001940 * @param cancelAnimators Should running animations be cancelled.
1941 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001942 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001943 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1944 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001945 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
1946 }
1947
1948 /**
1949 * Set the effective overScroll amount which will be directly reflected in the layout.
1950 *
Jason Monke59dc402018-08-16 12:05:01 -04001951 * @param amount The amount to overScroll by.
1952 * @param onTop Should the effect be applied on top of the scroller.
1953 * @param animate Should an animation be performed.
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001954 * @param cancelAnimators Should running animations be cancelled.
Jason Monke59dc402018-08-16 12:05:01 -04001955 * @param isRubberbanded The value which will be passed to
1956 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001957 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001958 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001959 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
1960 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001961 if (cancelAnimators) {
1962 mStateAnimator.cancelOverScrollAnimators(onTop);
1963 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001964 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001965 }
1966
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001967 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001968 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
1969 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001970 amount = Math.max(0, amount);
1971 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001972 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001973 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07001974 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001975 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001976 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001977 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001978 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001979 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001980 }
1981 }
1982
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001983 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001984 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001985 mExpandHelper.onlyObserveMovements(amount > 1.0f);
1986 if (mDontReportNextOverScroll) {
1987 mDontReportNextOverScroll = false;
1988 return;
1989 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02001990 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02001991 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02001992 }
1993 }
1994
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001995 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi290600a2014-05-30 17:02:20 +02001996 public void setOverscrollTopChangedListener(
1997 OnOverscrollTopChangedListener overscrollTopChangedListener) {
1998 mOverscrollTopChangedListener = overscrollTopChangedListener;
1999 }
2000
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002001 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002002 public float getCurrentOverScrollAmount(boolean top) {
2003 return mAmbientState.getOverScrollAmount(top);
2004 }
2005
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002006 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002007 public float getCurrentOverScrolledPixels(boolean top) {
Jason Monke59dc402018-08-16 12:05:01 -04002008 return top ? mOverScrolledTopPixels : mOverScrolledBottomPixels;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002009 }
2010
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002011 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002012 private void setOverScrolledPixels(float amount, boolean onTop) {
2013 if (onTop) {
2014 mOverScrolledTopPixels = amount;
2015 } else {
2016 mOverScrolledBottomPixels = amount;
2017 }
2018 }
2019
Gus Prevascdc98342019-01-14 14:29:44 -05002020 /**
2021 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
2022 * would cause us to exceed the provided maximum overscroll, springs back instead.
2023 *
2024 * @param deltaY The (signed) number of pixels to scroll.
2025 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2026 * the overscroll limit.
2027 */
2028 private void onCustomOverScrolledBy(int deltaY, boolean clampedY) {
2029 assert ANCHOR_SCROLLING;
Gus Prevas0fa58d62019-01-11 13:58:40 -05002030 mScrollAnchorViewY -= deltaY;
2031 // Treat animating scrolls differently; see #computeScroll() for why.
2032 if (!mScroller.isFinished()) {
Gus Prevascdc98342019-01-14 14:29:44 -05002033 if (clampedY) {
2034 springBack();
2035 } else {
2036 float overScrollTop = getCurrentOverScrollAmount(true /* top */);
2037 if (isScrolledToTop() && mScrollAnchorViewY > 0) {
2038 notifyOverscrollTopListener(mScrollAnchorViewY,
2039 isRubberbanded(true /* onTop */));
2040 } else {
2041 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true /* onTop */));
2042 }
2043 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002044 }
2045 updateScrollAnchor();
2046 updateOnScrollChange();
2047 }
2048
Gus Prevascdc98342019-01-14 14:29:44 -05002049 /**
2050 * Scrolls to the given position, overscrolling if needed. If called during a fling and the
2051 * position exceeds the provided maximum overscroll, springs back instead.
2052 *
2053 * @param scrollY The target scroll position.
2054 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2055 * the overscroll limit.
2056 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002057 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek9212de82017-02-06 16:04:28 -08002058 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002059 assert !ANCHOR_SCROLLING;
Selim Cinek67b22602014-03-10 15:40:16 +01002060 // Treat animating scrolls differently; see #computeScroll() for why.
2061 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07002062 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002063 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002064 springBack();
2065 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02002066 float overScrollTop = getCurrentOverScrollAmount(true);
2067 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002068 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002069 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002070 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002071 }
Selim Cinek67b22602014-03-10 15:40:16 +01002072 }
Selim Cinek67b22602014-03-10 15:40:16 +01002073 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08002074 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002075 }
2076 }
2077
Gus Prevascdc98342019-01-14 14:29:44 -05002078 /**
2079 * Springs back from an overscroll by stopping the {@link #mScroller} and animating the
2080 * overscroll amount back to zero.
2081 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002082 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002083 private void springBack() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002084 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002085 boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0;
2086 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
2087 boolean overscrolledBottom = maxPositiveScrollAmount < 0;
2088 if (overScrolledTop || overscrolledBottom) {
2089 float newAmount;
2090 if (overScrolledTop) {
2091 newAmount = mScrollAnchorViewY;
2092 mScrollAnchorViewY = 0;
2093 mDontReportNextOverScroll = true;
2094 } else {
2095 newAmount = -maxPositiveScrollAmount;
2096 mScrollAnchorViewY -= maxPositiveScrollAmount;
2097 }
2098 setOverScrollAmount(newAmount, overScrolledTop, false);
2099 setOverScrollAmount(0.0f, overScrolledTop, true);
2100 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002101 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002102 } else {
2103 int scrollRange = getScrollRange();
2104 boolean overScrolledTop = mOwnScrollY <= 0;
2105 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2106 if (overScrolledTop || overScrolledBottom) {
2107 boolean onTop;
2108 float newAmount;
2109 if (overScrolledTop) {
2110 onTop = true;
2111 newAmount = -mOwnScrollY;
2112 setOwnScrollY(0);
2113 mDontReportNextOverScroll = true;
2114 } else {
2115 onTop = false;
2116 newAmount = mOwnScrollY - scrollRange;
2117 setOwnScrollY(scrollRange);
2118 }
2119 setOverScrollAmount(newAmount, onTop, false);
2120 setOverScrollAmount(0.0f, onTop, true);
2121 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002122 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002123 }
2124 }
2125
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002126 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek67b22602014-03-10 15:40:16 +01002127 private int getScrollRange() {
felkachang529bfe62018-07-04 12:51:44 +08002128 // In current design, it only use the top HUN to treat all of HUNs
2129 // although there are more than one HUNs
2130 int contentHeight = mContentHeight;
2131 if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
2132 contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
2133 }
2134 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002135 int imeInset = getImeInset();
felkachang529bfe62018-07-04 12:51:44 +08002136 scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
Selim Cineka424c502016-04-05 13:07:54 -07002137 return scrollRange;
2138 }
2139
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002140 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cineka424c502016-04-05 13:07:54 -07002141 private int getImeInset() {
2142 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002143 }
2144
Selim Cinek343e6e22014-04-11 21:23:30 +02002145 /**
2146 * @return the first child which has visibility unequal to GONE
2147 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002148 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb55386d2015-12-16 17:26:49 -08002149 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002150 int childCount = getChildCount();
2151 for (int i = 0; i < childCount; i++) {
2152 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002153 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002154 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002155 }
2156 }
2157 return null;
2158 }
2159
Selim Cinek4a1ac842014-05-01 15:51:58 +02002160 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002161 * @return the child before the given view which has visibility unequal to GONE
2162 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002163 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002164 public ExpandableView getViewBeforeView(ExpandableView view) {
2165 ExpandableView previousView = null;
2166 int childCount = getChildCount();
2167 for (int i = 0; i < childCount; i++) {
2168 View child = getChildAt(i);
2169 if (child == view) {
2170 return previousView;
2171 }
2172 if (child.getVisibility() != View.GONE) {
2173 previousView = (ExpandableView) child;
2174 }
2175 }
2176 return null;
2177 }
2178
2179 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002180 * @return The first child which has visibility unequal to GONE which is currently below the
Jason Monke59dc402018-08-16 12:05:01 -04002181 * given translationY or equal to it.
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002182 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002183 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekef8c2252017-02-10 14:52:18 -08002184 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002185 int childCount = getChildCount();
2186 for (int i = 0; i < childCount; i++) {
2187 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002188 if (child.getVisibility() == View.GONE) {
2189 continue;
2190 }
2191 float rowTranslation = child.getTranslationY();
2192 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002193 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002194 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2195 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2196 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2197 List<ExpandableNotificationRow> notificationChildren =
2198 row.getNotificationChildren();
2199 for (int childIndex = 0; childIndex < notificationChildren.size();
2200 childIndex++) {
2201 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2202 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2203 return rowChild;
2204 }
2205 }
2206 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002207 }
2208 }
2209 return null;
2210 }
2211
2212 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002213 * @return the last child which has visibility unequal to GONE
2214 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002215 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002216 public ExpandableView getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002217 int childCount = getChildCount();
2218 for (int i = childCount - 1; i >= 0; i--) {
2219 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002220 if (child.getVisibility() != View.GONE && child != mShelf) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002221 return (ExpandableView) child;
Selim Cinek4a1ac842014-05-01 15:51:58 +02002222 }
2223 }
2224 return null;
2225 }
2226
Gus Prevas0fa58d62019-01-11 13:58:40 -05002227 private ExpandableNotificationRow getLastRowNotGone() {
2228 int childCount = getChildCount();
2229 for (int i = childCount - 1; i >= 0; i--) {
2230 View child = getChildAt(i);
2231 if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
2232 return (ExpandableNotificationRow) child;
2233 }
2234 }
2235 return null;
2236 }
2237
Jorim Jaggi069cd032014-05-15 03:09:01 +02002238 /**
2239 * @return the number of children which have visibility unequal to GONE
2240 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002241 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi069cd032014-05-15 03:09:01 +02002242 public int getNotGoneChildCount() {
2243 int childCount = getChildCount();
2244 int count = 0;
2245 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002246 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002247 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002248 count++;
2249 }
2250 }
2251 return count;
2252 }
2253
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002254 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01002255 private void updateContentHeight() {
2256 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002257 float previousPaddingRequest = mPaddingBetweenElements;
2258 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002259 int numShownItems = 0;
2260 boolean finish = false;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002261 int maxDisplayedNotifications = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002262 ? (hasPulsingNotifications() ? 1 : 0)
Adrian Roos7a9551a2017-01-11 12:27:49 -08002263 : mMaxDisplayedNotifications;
2264
Selim Cinek67b22602014-03-10 15:40:16 +01002265 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002266 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002267 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002268 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002269 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002270 boolean limitReached = maxDisplayedNotifications != -1
2271 && numShownItems >= maxDisplayedNotifications;
Lucas Dupin16cfe452018-02-08 13:14:50 -08002272 boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
Selim Cinekbe2c4432017-05-30 12:11:09 -07002273 && hasPulsingNotifications()
Adrian Roos7d062c42017-03-30 15:11:43 -07002274 && expandableView instanceof ExpandableNotificationRow
2275 && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
2276 if (limitReached || notificationOnAmbientThatIsNotPulsing) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002277 expandableView = mShelf;
2278 finish = true;
2279 }
Selim Cinek42357e02016-02-24 18:48:01 -08002280 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002281 float padding;
2282 if (increasedPaddingAmount >= 0.0f) {
2283 padding = (int) NotificationUtils.interpolate(
2284 previousPaddingRequest,
2285 mIncreasedPaddingBetweenElements,
2286 increasedPaddingAmount);
2287 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002288 mPaddingBetweenElements,
2289 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002290 increasedPaddingAmount);
2291 } else {
2292 int ownPadding = (int) NotificationUtils.interpolate(
2293 0,
2294 mPaddingBetweenElements,
2295 1.0f + increasedPaddingAmount);
2296 if (previousPaddingAmount > 0.0f) {
2297 padding = (int) NotificationUtils.interpolate(
2298 ownPadding,
2299 mIncreasedPaddingBetweenElements,
2300 previousPaddingAmount);
2301 } else {
2302 padding = ownPadding;
2303 }
2304 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002305 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002306 if (height != 0) {
2307 height += padding;
2308 }
2309 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002310 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002311 numShownItems++;
2312 if (finish) {
2313 break;
2314 }
Selim Cinek67b22602014-03-10 15:40:16 +01002315 }
2316 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002317 mIntrinsicContentHeight = height;
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002318
Lucas Dupin00be88f2019-01-03 17:50:52 -08002319 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002320 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002321 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002322 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002323 }
2324
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002325 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burnsf81c4c42019-01-07 14:10:43 -05002326 private boolean isPulsing(NotificationEntry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002327 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002328 }
2329
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002330 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002331 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbe2c4432017-05-30 12:11:09 -07002332 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002333 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002334 }
2335
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002336 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07002337 private void updateScrollability() {
Riddle Hsu065c01c2018-05-10 23:14:19 +08002338 boolean scrollable = !mQsExpanded && getScrollRange() > 0;
Selim Cinekc22fff62016-05-20 12:44:30 -07002339 if (scrollable != mScrollable) {
2340 mScrollable = scrollable;
2341 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002342 updateForwardAndBackwardScrollability();
2343 }
2344 }
2345
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002346 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef406062016-09-29 17:33:13 -07002347 private void updateForwardAndBackwardScrollability() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002348 boolean forwardScrollable = mScrollable && !isScrolledToBottom();
2349 boolean backwardsScrollable = mScrollable && !isScrolledToTop();
Selim Cinekef406062016-09-29 17:33:13 -07002350 boolean changed = forwardScrollable != mForwardScrollable
2351 || backwardsScrollable != mBackwardScrollable;
2352 mForwardScrollable = forwardScrollable;
2353 mBackwardScrollable = backwardsScrollable;
2354 if (changed) {
2355 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002356 }
Selim Cinek67b22602014-03-10 15:40:16 +01002357 }
2358
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002359 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002360 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002361 // No need to update the background color if it's not being drawn.
Lucas Dupin16cfe452018-02-08 13:14:50 -08002362 if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002363 return;
2364 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002365
Selim Cinek6811d722016-01-19 17:53:12 -08002366 updateBackgroundBounds();
Gus Prevase2d6f042018-10-17 15:25:30 -04002367 if (didSectionBoundsChange()) {
2368 boolean animate = mAnimateNextSectionBoundsChange || mAnimateNextBackgroundTop
2369 || mAnimateNextBackgroundBottom || areSectionBoundsAnimating();
Selim Cinek54680902016-10-19 16:49:44 -07002370 if (!isExpanded()) {
2371 abortBackgroundAnimators();
2372 animate = false;
2373 }
2374 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002375 startBackgroundAnimation();
2376 } else {
Gus Prevase2d6f042018-10-17 15:25:30 -04002377 for (NotificationSection section : mSections) {
2378 section.resetCurrentBounds();
2379 }
Lucas Dupin90a38dd2018-09-05 09:37:37 -07002380 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002381 }
2382 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002383 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002384 }
Selim Cinek614576e2016-01-20 10:54:09 -08002385 mAnimateNextBackgroundTop = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04002386 mAnimateNextBackgroundBottom = false;
2387 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08002388 }
2389
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002390 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek54680902016-10-19 16:49:44 -07002391 private void abortBackgroundAnimators() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002392 for (NotificationSection section : mSections) {
2393 section.cancelAnimators();
Selim Cinek54680902016-10-19 16:49:44 -07002394 }
2395 }
2396
Gus Prevase2d6f042018-10-17 15:25:30 -04002397 private boolean didSectionBoundsChange() {
2398 for (NotificationSection section : mSections) {
2399 if (section.didBoundsChange()) {
2400 return true;
2401 }
2402 }
2403 return false;
2404 }
2405
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002406 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevase2d6f042018-10-17 15:25:30 -04002407 private boolean areSectionBoundsAnimating() {
2408 for (NotificationSection section : mSections) {
2409 if (section.areBoundsAnimating()) {
2410 return true;
2411 }
2412 }
2413 return false;
Selim Cinek614576e2016-01-20 10:54:09 -08002414 }
2415
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002416 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08002417 private void startBackgroundAnimation() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002418 // TODO(kprevas): do we still need separate fields for top/bottom?
2419 // or can each section manage its own animation state?
2420 NotificationSection firstVisibleSection = getFirstVisibleSection();
2421 NotificationSection lastVisibleSection = getLastVisibleSection();
2422 for (NotificationSection section : mSections) {
2423 section.startBackgroundAnimation(
2424 section == firstVisibleSection
2425 ? mAnimateNextBackgroundTop
2426 : mAnimateNextSectionBoundsChange,
2427 section == lastVisibleSection
2428 ? mAnimateNextBackgroundBottom
2429 : mAnimateNextSectionBoundsChange);
Selim Cinek614576e2016-01-20 10:54:09 -08002430 }
Selim Cinek6811d722016-01-19 17:53:12 -08002431 }
2432
2433 /**
2434 * Update the background bounds to the new desired bounds
2435 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002436 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002437 private void updateBackgroundBounds() {
shawnlin3a2a2e22018-05-04 17:09:50 +08002438 getLocationInWindow(mTempInt2);
Gus Prevase2d6f042018-10-17 15:25:30 -04002439 int left = mTempInt2[0] + mSidePaddings;
2440 int right = mTempInt2[0] + getWidth() - mSidePaddings;
2441 for (NotificationSection section : mSections) {
2442 section.getBounds().left = left;
2443 section.getBounds().right = right;
2444 }
shawnlin3a2a2e22018-05-04 17:09:50 +08002445
Selim Cinek614576e2016-01-20 10:54:09 -08002446 if (!mIsExpanded) {
Gus Prevase2d6f042018-10-17 15:25:30 -04002447 for (NotificationSection section : mSections) {
2448 section.getBounds().top = 0;
2449 section.getBounds().bottom = 0;
2450 }
Selim Cinek1791f502016-10-07 17:38:03 -04002451 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002452 }
Gus Prevase83700cb2018-12-14 11:42:51 -05002453 int top = getSectionTopOrFinalTop(getFirstVisibleSection(), mAnimateNextBackgroundTop);
Gus Prevase2d6f042018-10-17 15:25:30 -04002454 NotificationSection lastSection = getLastVisibleSection();
Anthony Chen9e05d462017-04-07 10:10:21 -07002455 ActivatableNotificationView lastView =
2456 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
2457 ? mShelf
Gus Prevase2d6f042018-10-17 15:25:30 -04002458 : lastSection == null ? null : lastSection.getLastVisibleChild();
Anthony Chen9e05d462017-04-07 10:10:21 -07002459 int bottom;
Selim Cinek6811d722016-01-19 17:53:12 -08002460 if (lastView != null) {
Gus Prevase83700cb2018-12-14 11:42:51 -05002461 bottom = getSectionBottomOrFinalBottom(
2462 lastSection, lastView, mAnimateNextBackgroundBottom);
Selim Cinek3776fe02016-02-04 13:32:43 -08002463 } else {
Selim Cinekd393d5c2016-08-03 16:18:12 -07002464 top = mTopPadding;
Selim Cinek7db42982016-02-02 15:21:41 -08002465 bottom = top;
Selim Cinek6811d722016-01-19 17:53:12 -08002466 }
Selim Cinek355652a2016-12-07 13:32:12 -08002467 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinekd381bc32016-08-15 12:40:57 -07002468 top = (int) Math.max(mTopPadding + mStackTranslation, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002469 } else {
2470 // otherwise the animation from the shade to the keyguard will jump as it's maxed
Selim Cinekd381bc32016-08-15 12:40:57 -07002471 top = Math.max(0, top);
Selim Cinek3776fe02016-02-04 13:32:43 -08002472 }
Gus Prevase2d6f042018-10-17 15:25:30 -04002473 bottom = Math.max(bottom, top);
2474
2475 setSectionBoundsByPriority(left, right, top, bottom, mSections[0], mSections[1]);
2476 }
2477
Gus Prevase83700cb2018-12-14 11:42:51 -05002478 private int getSectionTopOrFinalTop(
2479 @Nullable NotificationSection section, boolean alreadyAnimating) {
2480 int top = 0;
2481 if (section != null) {
2482 ActivatableNotificationView firstView = section.getFirstVisibleChild();
Gus Prevas7c7f8c22019-01-18 14:21:19 -05002483 if (firstView != null) {
2484 // Round Y up to avoid seeing the background during animation
2485 int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
2486 if (alreadyAnimating || section.isTargetTop(finalTranslationY)) {
2487 // we're ending up at the same location as we are now, let's just skip the
2488 // animation
2489 top = finalTranslationY;
2490 } else {
2491 top = (int) Math.ceil(firstView.getTranslationY());
2492 }
Gus Prevase83700cb2018-12-14 11:42:51 -05002493 }
2494 }
2495 return top;
2496 }
2497
2498 private int getSectionBottomOrFinalBottom(
2499 @Nullable NotificationSection section, boolean alreadyAnimating) {
2500 return section == null ? 0
2501 : getSectionBottomOrFinalBottom(
2502 section, section.getLastVisibleChild(), alreadyAnimating);
2503 }
2504
2505 private int getSectionBottomOrFinalBottom(
2506 NotificationSection section,
2507 ActivatableNotificationView lastView,
2508 boolean alreadyAnimating) {
2509 int bottom = 0;
2510 if (lastView != null) {
2511 float finalTranslationY;
2512 if (lastView == mShelf) {
2513 finalTranslationY = mShelf.getTranslationY();
2514 } else {
2515 finalTranslationY = ViewState.getFinalTranslationY(lastView);
2516 }
2517 int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
2518 // Round Y down to avoid seeing the background during animation
2519 int finalBottom = (int) Math.floor(
2520 finalTranslationY + finalHeight - lastView.getClipBottomAmount());
2521 if (alreadyAnimating || section.isTargetBottom(finalBottom)) {
2522 // we're ending up at the same location as we are now, lets just skip the animation
2523 bottom = finalBottom;
2524 } else {
2525 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
2526 - lastView.getClipBottomAmount());
2527 }
2528 }
2529 return bottom;
2530 }
2531
Gus Prevase2d6f042018-10-17 15:25:30 -04002532 private void setSectionBoundsByPriority(int left, int right, int top, int bottom,
2533 NotificationSection highPrioritySection, NotificationSection lowPrioritySection) {
2534 if (NotificationUtils.useNewInterruptionModel(mContext)) {
2535 // TODO(kprevas): can we use section boundary indices from mAmbientState instead?
2536 ActivatableNotificationView lastChildAboveGap = getLastHighPriorityChild();
2537 ActivatableNotificationView firstChildBelowGap = getFirstLowPriorityChild();
2538 if (lastChildAboveGap != null && firstChildBelowGap != null) {
Gus Prevase83700cb2018-12-14 11:42:51 -05002539 int gapTop = getSectionBottomOrFinalBottom(
2540 highPrioritySection, mAnimateNextSectionBoundsChange);
2541 gapTop = Math.max(top, Math.min(gapTop, bottom));
2542
2543 int gapBottom = getSectionTopOrFinalTop(
2544 lowPrioritySection, mAnimateNextSectionBoundsChange);
2545 gapBottom = Math.max(top, Math.min(gapBottom, bottom));
2546
Gus Prevase2d6f042018-10-17 15:25:30 -04002547 highPrioritySection.getBounds().set(left, top, right, gapTop);
2548 lowPrioritySection.getBounds().set(left, gapBottom, right, bottom);
2549 } else if (lastChildAboveGap != null) {
2550 highPrioritySection.getBounds().set(left, top, right, bottom);
2551 lowPrioritySection.getBounds().set(left, bottom, right, bottom);
2552 } else {
2553 highPrioritySection.getBounds().set(left, top, right, top);
2554 lowPrioritySection.getBounds().set(left, top, right, bottom);
2555 }
2556 } else {
2557 highPrioritySection.getBounds().set(left, top, right, bottom);
2558 lowPrioritySection.getBounds().set(left, bottom, right, bottom);
2559 }
Selim Cinek6811d722016-01-19 17:53:12 -08002560 }
2561
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002562 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002563 private ActivatableNotificationView getFirstPinnedHeadsUp() {
2564 int childCount = getChildCount();
2565 for (int i = 0; i < childCount; i++) {
2566 View child = getChildAt(i);
2567 if (child.getVisibility() != View.GONE
2568 && child instanceof ExpandableNotificationRow) {
2569 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2570 if (row.isPinned()) {
2571 return row;
2572 }
2573 }
2574 }
2575 return null;
2576 }
2577
Gus Prevase2d6f042018-10-17 15:25:30 -04002578 private NotificationSection getFirstVisibleSection() {
2579 for (NotificationSection section : mSections) {
2580 if (section.getFirstVisibleChild() != null) {
2581 return section;
2582 }
2583 }
2584 return null;
2585 }
2586
2587 private NotificationSection getLastVisibleSection() {
2588 for (int i = mSections.length - 1; i >= 0; i--) {
2589 NotificationSection section = mSections[i];
2590 if (section.getLastVisibleChild() != null) {
2591 return section;
2592 }
2593 }
2594 return null;
2595 }
2596
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002597 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002598 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002599 int childCount = getChildCount();
2600 for (int i = childCount - 1; i >= 0; i--) {
2601 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002602 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2603 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002604 return (ActivatableNotificationView) child;
2605 }
2606 }
2607 return null;
2608 }
2609
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002610 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002611 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002612 int childCount = getChildCount();
2613 for (int i = 0; i < childCount; i++) {
2614 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002615 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2616 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002617 return (ActivatableNotificationView) child;
2618 }
2619 }
2620 return null;
2621 }
2622
Gus Prevase2d6f042018-10-17 15:25:30 -04002623 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
2624 @Nullable
2625 private ActivatableNotificationView getLastHighPriorityChild() {
2626 ActivatableNotificationView lastChildBeforeGap = null;
2627 int childCount = getChildCount();
2628 for (int i = 0; i < childCount; i++) {
2629 View child = getChildAt(i);
2630 if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
2631 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Gus Prevascaed15c2019-01-18 14:19:51 -05002632 if (!row.getEntry().isHighPriority()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04002633 break;
2634 } else {
2635 lastChildBeforeGap = row;
2636 }
2637 }
2638 }
2639 return lastChildBeforeGap;
2640 }
2641
2642 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
2643 @Nullable
2644 private ActivatableNotificationView getFirstLowPriorityChild() {
2645 int childCount = getChildCount();
2646 for (int i = 0; i < childCount; i++) {
2647 View child = getChildAt(i);
2648 if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
2649 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Gus Prevascaed15c2019-01-18 14:19:51 -05002650 if (!row.getEntry().isHighPriority()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04002651 return row;
2652 }
2653 }
2654 }
2655 return null;
2656 }
2657
Selim Cinek67b22602014-03-10 15:40:16 +01002658 /**
2659 * Fling the scroll view
2660 *
2661 * @param velocityY The initial velocity in the Y direction. Positive
2662 * numbers mean that the finger/cursor is moving down the screen,
2663 * which means we want to scroll towards the top.
2664 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002665 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Muyuan Li26e30ae2016-04-11 17:31:42 -07002666 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002667 if (getChildCount() > 0) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002668 float topAmount = getCurrentOverScrollAmount(true);
2669 float bottomAmount = getCurrentOverScrollAmount(false);
2670 if (velocityY < 0 && topAmount > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002671 if (ANCHOR_SCROLLING) {
2672 mScrollAnchorViewY += topAmount;
2673 } else {
2674 setOwnScrollY(mOwnScrollY - (int) topAmount);
2675 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002676 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002677 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002678 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002679 * mOverflingDistance + topAmount;
2680 } else if (velocityY > 0 && bottomAmount > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002681 if (ANCHOR_SCROLLING) {
2682 mScrollAnchorViewY -= bottomAmount;
2683 } else {
2684 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
2685 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002686 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002687 mMaxOverScroll = Math.abs(velocityY) / 1000f
2688 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
Jason Monke59dc402018-08-16 12:05:01 -04002689 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002690 } else {
2691 // it will be set once we reach the boundary
2692 mMaxOverScroll = 0.0f;
2693 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002694 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002695 flingScroller(velocityY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05002696 } else {
2697 int scrollRange = getScrollRange();
2698 int minScrollY = Math.max(0, scrollRange);
2699 if (mExpandedInThisMotion) {
2700 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2701 }
2702 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2703 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek94ab18c2016-02-25 12:35:51 -08002704 }
Selim Cinek67b22602014-03-10 15:40:16 +01002705
Selim Cinek9212de82017-02-06 16:04:28 -08002706 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002707 }
2708 }
2709
Selim Cinek1408eb52014-06-02 14:45:38 +02002710 /**
Gus Prevascdc98342019-01-14 14:29:44 -05002711 * Flings the overscroller with the given velocity (anchor-based scrolling).
2712 *
2713 * Because anchor-based scrolling can't track the current scroll position, the overscroller is
2714 * always started at startY = 0, and we interpret the positions it computes as relative to the
2715 * start of the scroll.
2716 */
2717 private void flingScroller(int velocityY) {
2718 assert ANCHOR_SCROLLING;
2719 mIsScrollerBoundSet = false;
2720 maybeFlingScroller(velocityY, true /* always fling */);
2721 }
2722
2723 private void maybeFlingScroller(int velocityY, boolean alwaysFling) {
2724 assert ANCHOR_SCROLLING;
2725 // Attempt to determine the maximum amount to scroll before we reach the end.
2726 // If the first view is not materialized (for an upwards scroll) or the last view is either
2727 // not materialized or is pinned to the shade (for a downwards scroll), we don't know this
2728 // amount, so we do an unbounded fling and rely on {@link #maybeReflingScroller()} to update
2729 // the scroller once we approach the start/end of the list.
2730 int minY = Integer.MIN_VALUE;
2731 int maxY = Integer.MAX_VALUE;
2732 if (velocityY < 0) {
2733 minY = getMaxNegativeScrollAmount();
2734 if (minY > Integer.MIN_VALUE) {
2735 mIsScrollerBoundSet = true;
2736 }
2737 } else {
2738 maxY = getMaxPositiveScrollAmount();
2739 if (maxY < Integer.MAX_VALUE) {
2740 mIsScrollerBoundSet = true;
2741 }
2742 }
2743 if (mIsScrollerBoundSet || alwaysFling) {
2744 mLastScrollerY = 0;
2745 // x velocity is set to 1 to avoid overscroller bug
2746 mScroller.fling(0, 0, 1, velocityY, 0, 0, minY, maxY, 0,
2747 mExpandedInThisMotion && !isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2);
2748 }
2749 }
2750
2751 /**
2752 * Returns the maximum number of pixels we can scroll in the positive direction (downwards)
2753 * before reaching the bottom of the list (discounting overscroll).
2754 *
2755 * If the return value is negative then we have overscrolled; this is a transient state which
2756 * should immediately be handled by adjusting the anchor position and adding the extra space to
2757 * the bottom overscroll amount.
2758 *
2759 * If we don't know how many pixels we have left to scroll (because the last row has not been
2760 * materialized, or it's in the shelf so it doesn't have its "natural" position), we return
2761 * {@link Integer#MAX_VALUE}.
2762 */
2763 private int getMaxPositiveScrollAmount() {
2764 assert ANCHOR_SCROLLING;
2765 // TODO: once we're recycling we need to check the adapter position of the last child.
2766 ExpandableNotificationRow lastRow = getLastRowNotGone();
2767 if (mScrollAnchorView != null && lastRow != null && !lastRow.isInShelf()) {
2768 // distance from bottom of last child to bottom of notifications area is:
2769 // distance from bottom of last child
2770 return (int) (lastRow.getTranslationY() + lastRow.getActualHeight()
2771 // to top of anchor view
2772 - mScrollAnchorView.getTranslationY()
2773 // plus distance from anchor view to top of notifications area
2774 + mScrollAnchorViewY
2775 // minus height of notifications area.
2776 - (mMaxLayoutHeight - getIntrinsicPadding() - mFooterView.getActualHeight()));
2777 } else {
2778 return Integer.MAX_VALUE;
2779 }
2780 }
2781
2782 /**
2783 * Returns the maximum number of pixels (as a negative number) we can scroll in the negative
2784 * direction (upwards) before reaching the top of the list (discounting overscroll).
2785 *
2786 * If the return value is positive then we have overscrolled; this is a transient state which
2787 * should immediately be handled by adjusting the anchor position and adding the extra space to
2788 * the top overscroll amount.
2789 *
2790 * If we don't know how many pixels we have left to scroll (because the first row has not been
2791 * materialized), we return {@link Integer#MIN_VALUE}.
2792 */
2793 private int getMaxNegativeScrollAmount() {
2794 assert ANCHOR_SCROLLING;
2795 // TODO: once we're recycling we need to check the adapter position of the first child.
2796 ExpandableView firstChild = getFirstChildNotGone();
2797 if (mScrollAnchorView != null && firstChild != null) {
2798 // distance from top of first child to top of notifications area is:
2799 // distance from top of anchor view
2800 return (int) -(mScrollAnchorView.getTranslationY()
2801 // to top of first child
2802 - firstChild.getTranslationY()
2803 // minus distance from top of anchor view to top of notifications area.
2804 - mScrollAnchorViewY);
2805 } else {
2806 return Integer.MIN_VALUE;
2807 }
2808 }
2809
2810 /**
2811 * During a fling, if we were unable to set the bounds of the fling due to the top/bottom view
2812 * not being materialized or being pinned to the shelf, we need to check on every frame if we're
2813 * able to set the bounds. If we are, we fling the scroller again with the newly computed
2814 * bounds.
2815 */
2816 private void maybeReflingScroller() {
2817 if (!mIsScrollerBoundSet) {
2818 // Because mScroller is a flywheel scroller, we fling with the minimum possible
2819 // velocity to establish direction, so as not to perceptibly affect the velocity.
2820 maybeFlingScroller((int) Math.signum(mScroller.getCurrVelocity()),
2821 false /* alwaysFling */);
2822 }
2823 }
2824
2825 /**
Selim Cinek1408eb52014-06-02 14:45:38 +02002826 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2827 * overScroll view (i.e QS).
2828 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002829 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02002830 private boolean shouldOverScrollFling(int initialVelocity) {
2831 float topOverScroll = getCurrentOverScrollAmount(true);
2832 return mScrolledToTopOnFirstDown
2833 && !mExpandedInThisMotion
2834 && topOverScroll > mMinTopOverScrollToEscape
2835 && initialVelocity > 0;
2836 }
2837
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002838 /**
2839 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2840 * account.
2841 *
Jason Monke59dc402018-08-16 12:05:01 -04002842 * @param qsHeight the top padding imposed by the quick settings panel
2843 * @param animate whether to animate the change
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002844 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2845 * {@code qsHeight} is the final top padding
2846 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002847 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jason Monk16ac3772016-02-10 15:39:21 -05002848 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002849 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002850 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002851 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002852 if (topPadding + minStackHeight > getHeight()) {
2853 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002854 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002855 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002856 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002857 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002858 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002859 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002860 }
2861
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002862 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
shawnlin8e4e92c2018-04-12 18:47:24 +08002863 public void setMaxTopPadding(int maxTopPadding) {
2864 mMaxTopPadding = maxTopPadding;
2865 }
2866
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002867 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002868 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002869 if (isHeadsUpTransition()) {
2870 return getTopHeadsUpPinnedHeight();
2871 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002872 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002873 }
2874
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002875 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002876 public float getTopPaddingOverflow() {
2877 return mTopPaddingOverflow;
2878 }
2879
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002880 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002881 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002882 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002883 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002884 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002885 int shelfHeight = 0;
Gus Prevase2d6f042018-10-17 15:25:30 -04002886 if (getLastVisibleSection() != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002887 shelfHeight = mShelf.getIntrinsicHeight();
2888 }
2889 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002890 }
2891
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002892 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02002893 private int clampPadding(int desiredPadding) {
2894 return Math.max(desiredPadding, mIntrinsicPadding);
2895 }
2896
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002897 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfed1ab62014-06-17 14:10:33 -07002898 private float getRubberBandFactor(boolean onTop) {
2899 if (!onTop) {
2900 return RUBBER_BAND_FACTOR_NORMAL;
2901 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002902 if (mExpandedInThisMotion) {
2903 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002904 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002905 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2906 } else if (mScrolledToTopOnFirstDown) {
2907 return 1.0f;
2908 }
2909 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002910 }
2911
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002912 /**
2913 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2914 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2915 * overscroll view (e.g. expand QS).
2916 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002917 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002918 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002919 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002920 || !mScrolledToTopOnFirstDown;
2921 }
2922
Selim Cinek67b22602014-03-10 15:40:16 +01002923
Selim Cinek67b22602014-03-10 15:40:16 +01002924
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002925 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef5127e2015-12-21 16:55:58 -08002926 public void setChildTransferInProgress(boolean childTransferInProgress) {
2927 mChildTransferInProgress = childTransferInProgress;
2928 }
2929
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002930 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Christoph Studer068f5922014-04-08 17:43:07 -04002931 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002932 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002933 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002934 // we only call our internal methods if this is actually a removal and not just a
2935 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002936 if (!mChildTransferInProgress) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002937 onViewRemovedInternal((ExpandableView) child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002938 }
2939 }
2940
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002941 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002942 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -05002943 public void cleanUpViewStateForEntry(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04002944 View child = entry.getRow();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04002945 if (child == mSwipeHelper.getTranslatingParentView()) {
2946 mSwipeHelper.clearTranslatingParentView();
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002947 }
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002948 }
2949
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002950 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002951 private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002952 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002953 // This is only a position change, don't do anything special
2954 return;
2955 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002956 child.setOnHeightChangedListener(null);
2957 updateScrollStateForRemovedChild(child);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002958 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002959 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002960 if (!mSwipedOutViews.contains(child)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002961 || Math.abs(child.getTranslation()) != child.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002962 container.addTransientView(child, 0);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002963 child.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002964 }
2965 } else {
2966 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002967 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002968 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002969
Selim Cineke9bad242016-06-15 11:46:37 -07002970 focusNextViewIfFocused(child);
2971 }
2972
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002973 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke9bad242016-06-15 11:46:37 -07002974 private void focusNextViewIfFocused(View view) {
2975 if (view instanceof ExpandableNotificationRow) {
2976 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2977 if (row.shouldRefocusOnDismiss()) {
2978 View nextView = row.getChildAfterViewWhenDismissed();
2979 if (nextView == null) {
2980 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2981 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2982 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002983 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002984 }
2985 if (nextView != null) {
2986 nextView.requestAccessibilityFocus();
2987 }
2988 }
2989 }
2990
Selim Cinekc27437b2014-05-14 10:23:33 +02002991 }
2992
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002993 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01002994 private boolean isChildInGroup(View child) {
2995 return child instanceof ExpandableNotificationRow
2996 && mGroupManager.isChildInGroupWithSummary(
Jason Monke59dc402018-08-16 12:05:01 -04002997 ((ExpandableNotificationRow) child).getStatusBarNotification());
Selim Cinekb5605e52015-02-20 18:21:41 +01002998 }
2999
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003000 /**
3001 * Generate a remove animation for a child view.
3002 *
3003 * @param child The view to generate the remove animation for.
3004 * @return Whether an animation was generated.
3005 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003006 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003007 private boolean generateRemoveAnimation(ExpandableView child) {
Selim Cineke0890e52015-06-17 11:17:08 -07003008 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07003009 mAddedHeadsUpChildren.remove(child);
3010 return false;
3011 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003012 if (isClickedHeadsUp(child)) {
Selim Cinek9dd0d042018-05-14 18:12:42 -07003013 // An animation is already running, add it transiently
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003014 mClearTransientViewsWhenFinished.add(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07003015 return true;
3016 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003017 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07003018 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02003019 // Generate Animations
3020 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003021 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003022 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02003023 } else {
3024 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003025 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003026 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02003027 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003028 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003029 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003030 }
3031
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003032 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek0fccc722015-07-29 17:04:36 -07003033 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09003034 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07003035 }
3036
Selim Cineke0890e52015-06-17 11:17:08 -07003037 /**
3038 * Remove a removed child view from the heads up animations if it was just added there
3039 *
3040 * @return whether any child was removed from the list to animate
3041 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003042 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke0890e52015-06-17 11:17:08 -07003043 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
3044 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003045 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
3046 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07003047 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003048 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07003049 mTmpList.add(eventPair);
3050 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003051 }
3052 }
Selim Cineke0890e52015-06-17 11:17:08 -07003053 if (hasAddEvent) {
3054 // This child was just added lets remove all events.
3055 mHeadsUpChangeAnimations.removeAll(mTmpList);
Jason Monke59dc402018-08-16 12:05:01 -04003056 ((ExpandableNotificationRow) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07003057 }
3058 mTmpList.clear();
3059 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003060 }
3061
Selim Cinek572bbd42014-04-25 16:43:27 +02003062 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01003063 * @param child the child to query
3064 * @return whether a view is not a top level child but a child notification and that group is
Jason Monke59dc402018-08-16 12:05:01 -04003065 * not expanded
Selim Cinekb5605e52015-02-20 18:21:41 +01003066 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003067 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003068 private boolean isChildInInvisibleGroup(View child) {
3069 if (child instanceof ExpandableNotificationRow) {
3070 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Ned Burnsf81c4c42019-01-07 14:10:43 -05003071 NotificationEntry groupSummary =
Selim Cinekb5605e52015-02-20 18:21:41 +01003072 mGroupManager.getGroupSummary(row.getStatusBarNotification());
Evan Laird94492852018-10-25 13:43:01 -04003073 if (groupSummary != null && groupSummary.getRow() != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07003074 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01003075 }
3076 }
3077 return false;
3078 }
3079
3080 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02003081 * Updates the scroll position when a child was removed
3082 *
3083 * @param removedChild the removed child
3084 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003085 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek61633a82016-01-25 15:54:10 -08003086 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003087 if (ANCHOR_SCROLLING) {
3088 if (removedChild == mScrollAnchorView) {
3089 ExpandableView firstChild = getFirstChildNotGone();
3090 if (firstChild != null) {
3091 mScrollAnchorView = firstChild;
3092 } else {
3093 mScrollAnchorView = mShelf;
3094 }
3095 // Adjust anchor view Y by the distance between the old and new anchors
3096 // so that there's no visible change.
3097 mScrollAnchorViewY +=
3098 mScrollAnchorView.getTranslationY() - removedChild.getTranslationY();
3099 }
3100 updateScrollAnchor();
3101 // TODO: once we're recycling this will need to check the adapter position of the child
3102 if (mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY > 0) {
3103 mScrollAnchorViewY = 0;
3104 }
3105 updateOnScrollChange();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003106 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003107 int startingPosition = getPositionInLinearLayout(removedChild);
3108 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
3109 int padding;
3110 if (increasedPaddingAmount >= 0) {
3111 padding = (int) NotificationUtils.interpolate(
3112 mPaddingBetweenElements,
3113 mIncreasedPaddingBetweenElements,
3114 increasedPaddingAmount);
3115 } else {
3116 padding = (int) NotificationUtils.interpolate(
3117 0,
3118 mPaddingBetweenElements,
3119 1.0f + increasedPaddingAmount);
3120 }
3121 int childHeight = getIntrinsicHeight(removedChild) + padding;
3122 int endPosition = startingPosition + childHeight;
3123 if (endPosition <= mOwnScrollY) {
3124 // This child is fully scrolled of the top, so we have to deduct its height from the
3125 // scrollPosition
3126 setOwnScrollY(mOwnScrollY - childHeight);
3127 } else if (startingPosition < mOwnScrollY) {
3128 // This child is currently being scrolled into, set the scroll position to the
3129 // start of this child
3130 setOwnScrollY(startingPosition);
3131 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003132 }
3133 }
3134
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003135 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd7c4e002014-07-04 18:36:42 +02003136 private int getIntrinsicHeight(View view) {
3137 if (view instanceof ExpandableView) {
3138 ExpandableView expandableView = (ExpandableView) view;
3139 return expandableView.getIntrinsicHeight();
3140 }
3141 return view.getHeight();
3142 }
3143
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003144 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1f624952017-06-08 19:11:50 -07003145 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07003146 ExpandableNotificationRow childInGroup = null;
3147 ExpandableNotificationRow requestedRow = null;
3148 if (isChildInGroup(requestedView)) {
3149 // We're asking for a child in a group. Calculate the position of the parent first,
3150 // then within the parent.
3151 childInGroup = (ExpandableNotificationRow) requestedView;
3152 requestedView = requestedRow = childInGroup.getNotificationParent();
3153 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003154 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08003155 float previousPaddingRequest = mPaddingBetweenElements;
3156 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02003157 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08003158 ExpandableView child = (ExpandableView) getChildAt(i);
3159 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07003160 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08003161 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003162 float padding;
3163 if (increasedPaddingAmount >= 0.0f) {
3164 padding = (int) NotificationUtils.interpolate(
3165 previousPaddingRequest,
3166 mIncreasedPaddingBetweenElements,
3167 increasedPaddingAmount);
3168 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08003169 mPaddingBetweenElements,
3170 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08003171 increasedPaddingAmount);
3172 } else {
3173 int ownPadding = (int) NotificationUtils.interpolate(
3174 0,
3175 mPaddingBetweenElements,
3176 1.0f + increasedPaddingAmount);
3177 if (previousPaddingAmount > 0.0f) {
3178 padding = (int) NotificationUtils.interpolate(
3179 ownPadding,
3180 mIncreasedPaddingBetweenElements,
3181 previousPaddingAmount);
3182 } else {
3183 padding = ownPadding;
3184 }
3185 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08003186 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08003187 if (position != 0) {
3188 position += padding;
3189 }
3190 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08003191 }
Adrian Roos4a579672016-05-24 16:54:37 -07003192 if (child == requestedView) {
3193 if (requestedRow != null) {
3194 position += requestedRow.getPositionOfChild(childInGroup);
3195 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003196 return position;
3197 }
Selim Cinek61633a82016-01-25 15:54:10 -08003198 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02003199 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003200 }
3201 }
3202 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02003203 }
3204
3205 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003206 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adam Powell6690d012015-06-17 16:41:56 -07003207 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02003208 super.onViewAdded(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003209 onViewAddedInternal((ExpandableView) child);
Selim Cinekb5605e52015-02-20 18:21:41 +01003210 }
3211
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003212 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08003213 private void updateFirstAndLastBackgroundViews() {
Gus Prevase2d6f042018-10-17 15:25:30 -04003214 NotificationSection firstSection = getFirstVisibleSection();
3215 NotificationSection lastSection = getLastVisibleSection();
Gus Prevasda13cfa2018-11-20 14:58:48 -05003216 ActivatableNotificationView previousFirstChild =
3217 firstSection == null ? null : firstSection.getFirstVisibleChild();
3218 ActivatableNotificationView previousLastChild =
3219 lastSection == null ? null : lastSection.getLastVisibleChild();
Gus Prevase2d6f042018-10-17 15:25:30 -04003220
Selim Cinek614576e2016-01-20 10:54:09 -08003221 ActivatableNotificationView firstChild = getFirstChildWithBackground();
3222 ActivatableNotificationView lastChild = getLastChildWithBackground();
Gus Prevase2d6f042018-10-17 15:25:30 -04003223 boolean sectionViewsChanged = updateFirstAndLastViewsInSectionsByPriority(
3224 mSections[0], mSections[1], firstChild, lastChild);
3225
Selim Cinek614576e2016-01-20 10:54:09 -08003226 if (mAnimationsEnabled && mIsExpanded) {
Gus Prevasda13cfa2018-11-20 14:58:48 -05003227 mAnimateNextBackgroundTop = firstChild != previousFirstChild;
3228 mAnimateNextBackgroundBottom = lastChild != previousLastChild;
Gus Prevase2d6f042018-10-17 15:25:30 -04003229 mAnimateNextSectionBoundsChange = sectionViewsChanged;
Selim Cinek614576e2016-01-20 10:54:09 -08003230 } else {
3231 mAnimateNextBackgroundTop = false;
3232 mAnimateNextBackgroundBottom = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04003233 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08003234 }
Selim Cinekdb167372016-11-17 15:41:17 -08003235 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Gus Prevase2d6f042018-10-17 15:25:30 -04003236 mRoundnessManager.updateRoundedChildren(mSections);
Selim Cinek515b2032017-11-15 10:20:19 -08003237 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003238 }
3239
Gus Prevase2d6f042018-10-17 15:25:30 -04003240 /** @return {@code true} if the last view in the top section changed (so we need to animate). */
3241 private boolean updateFirstAndLastViewsInSectionsByPriority(
3242 final NotificationSection highPrioritySection,
3243 final NotificationSection lowPrioritySection,
3244 ActivatableNotificationView firstChild,
3245 ActivatableNotificationView lastChild) {
3246 if (NotificationUtils.useNewInterruptionModel(mContext)) {
3247 ActivatableNotificationView previousLastHighPriorityChild =
3248 highPrioritySection.getLastVisibleChild();
3249 ActivatableNotificationView previousFirstLowPriorityChild =
3250 lowPrioritySection.getFirstVisibleChild();
3251 ActivatableNotificationView lastHighPriorityChild = getLastHighPriorityChild();
3252 ActivatableNotificationView firstLowPriorityChild = getFirstLowPriorityChild();
3253 if (lastHighPriorityChild != null && firstLowPriorityChild != null) {
3254 highPrioritySection.setFirstVisibleChild(firstChild);
3255 highPrioritySection.setLastVisibleChild(lastHighPriorityChild);
3256 lowPrioritySection.setFirstVisibleChild(firstLowPriorityChild);
3257 lowPrioritySection.setLastVisibleChild(lastChild);
3258 } else if (lastHighPriorityChild != null) {
3259 highPrioritySection.setFirstVisibleChild(firstChild);
3260 highPrioritySection.setLastVisibleChild(lastChild);
3261 lowPrioritySection.setFirstVisibleChild(null);
3262 lowPrioritySection.setLastVisibleChild(null);
3263 } else {
3264 highPrioritySection.setFirstVisibleChild(null);
3265 highPrioritySection.setLastVisibleChild(null);
3266 lowPrioritySection.setFirstVisibleChild(firstChild);
3267 lowPrioritySection.setLastVisibleChild(lastChild);
3268 }
3269 return lastHighPriorityChild != previousLastHighPriorityChild
3270 || firstLowPriorityChild != previousFirstLowPriorityChild;
3271 } else {
3272 highPrioritySection.setFirstVisibleChild(firstChild);
3273 highPrioritySection.setLastVisibleChild(lastChild);
3274 return false;
3275 }
3276 }
3277
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003278 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003279 private void onViewAddedInternal(ExpandableView child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003280 updateHideSensitiveForChild(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003281 child.setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003282 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003283 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003284 updateChronometerForChild(child);
Gus Prevasa18dc572019-01-14 16:11:22 -05003285 if (child instanceof ExpandableNotificationRow) {
3286 ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
3287 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05003288 if (ANCHOR_SCROLLING) {
3289 // TODO: once we're recycling this will need to check the adapter position of the child
3290 if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) {
3291 // New child was added at the top while we're scrolled to the top;
3292 // make it the new anchor view so that we stay at the top.
3293 mScrollAnchorView = child;
3294 }
3295 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003296 }
3297
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003298 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003299 private void updateHideSensitiveForChild(ExpandableView child) {
3300 child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003301 }
3302
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003303 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003304 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003305 public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
Selim Cinekd1395642016-04-28 12:22:42 -07003306 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003307 }
3308
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003309 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003310 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003311 public void notifyGroupChildAdded(ExpandableView row) {
Selim Cinekb5605e52015-02-20 18:21:41 +01003312 onViewAddedInternal(row);
3313 }
3314
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003315 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003316 public void setAnimationsEnabled(boolean animationsEnabled) {
3317 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003318 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003319 if (!animationsEnabled) {
3320 mSwipedOutViews.clear();
3321 mChildrenToRemoveAnimated.clear();
3322 clearTemporaryViewsInGroup(this);
3323 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003324 }
3325
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003326 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003327 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003328 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003329 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003330 int childCount = getChildCount();
3331 for (int i = 0; i < childCount; i++) {
3332 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003333 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003334 updateAnimationState(running, child);
3335 }
3336 }
3337
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003338 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek51ae05d2014-09-09 15:51:38 +02003339 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003340 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003341 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003342 }
3343
Selim Cinek2627d722018-01-19 12:16:49 -08003344 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003345 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek2627d722018-01-19 12:16:49 -08003346 public void setExpandingNotification(ExpandableNotificationRow row) {
3347 mAmbientState.setExpandingNotification(row);
3348 requestChildrenUpdate();
3349 }
3350
3351 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003352 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek8875de12018-03-22 10:14:32 -07003353 public void bindRow(ExpandableNotificationRow row) {
Selim Cinekf0c79e12018-05-14 17:17:31 -07003354 row.setHeadsUpAnimatingAwayListener(animatingAway -> {
3355 mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
3356 mHeadsUpAppearanceController.updateHeader(row.getEntry());
3357 });
Selim Cinek8875de12018-03-22 10:14:32 -07003358 }
3359
3360 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003361 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek2627d722018-01-19 12:16:49 -08003362 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3363 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3364 requestChildrenUpdate();
3365 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003366
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003367 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003368 private void updateAnimationState(boolean running, View child) {
3369 if (child instanceof ExpandableNotificationRow) {
3370 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3371 row.setIconAnimationRunning(running);
3372 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003373 }
3374
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003375 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003376 public boolean isAddOrRemoveAnimationPending() {
3377 return mNeedsAnimation
3378 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3379 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003380
3381 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003382 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003383 public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003384 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003385 // Generate Animations
3386 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003387 if (fromMoreCard) {
3388 mFromMoreCardAdditions.add(child);
3389 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003390 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003391 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003392 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003393 mAddedHeadsUpChildren.add(child);
3394 mChildrenToAddAnimated.remove(child);
3395 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003396 }
3397
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003398 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003399 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003400 public void changeViewPosition(ExpandableView child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003401 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003402
3403 if (currentIndex == -1) {
3404 boolean isTransient = false;
3405 if (child instanceof ExpandableNotificationRow
Jason Monke59dc402018-08-16 12:05:01 -04003406 && ((ExpandableNotificationRow) child).getTransientContainer() != null) {
Rohan Shah8ee53652018-04-05 11:13:50 -07003407 isTransient = true;
3408 }
3409 Log.e(TAG, "Attempting to re-position "
3410 + (isTransient ? "transient" : "")
3411 + " view {"
3412 + child
3413 + "}");
3414 return;
3415 }
3416
Dan Sandlereceda3d2014-07-21 15:35:01 -04003417 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003418 mChangePositionInProgress = true;
Jason Monke59dc402018-08-16 12:05:01 -04003419 ((ExpandableView) child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003420 removeView(child);
3421 addView(child, newIndex);
Jason Monke59dc402018-08-16 12:05:01 -04003422 ((ExpandableView) child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003423 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003424 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003425 mChildrenChangingPositions.add(child);
3426 mNeedsAnimation = true;
3427 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003428 }
3429 }
3430
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003431 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf4c19962014-05-01 21:55:31 +02003432 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003433 if (mNeedsAnimation) {
Rohan Shahb9d500a2018-06-25 16:27:16 -07003434 generateAllAnimationEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003435 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003436 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003437 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003438 setAnimationRunning(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003439 mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003440 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003441 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003442 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003443 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003444 } else {
3445 applyCurrentState();
3446 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003447 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003448 }
3449
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003450 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shahb9d500a2018-06-25 16:27:16 -07003451 private void generateAllAnimationEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003452 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003453 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003454 generateChildAdditionEvents();
3455 generatePositionChangeEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003456 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003457 generateActivateEvent();
3458 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003459 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003460 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003461 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003462 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003463 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003464 generateAnimateEverythingEvent();
Lucas Dupin4e023812018-04-02 21:19:23 -07003465 generatePulsingAnimationEvent();
Selim Cinek572bbd42014-04-25 16:43:27 +02003466 }
3467
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003468 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003469 private void generateHeadsUpAnimationEvents() {
3470 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003471 ExpandableNotificationRow row = eventPair.first;
3472 boolean isHeadsUp = eventPair.second;
3473 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3474 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003475 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003476 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003477 type = row.wasJustClicked()
3478 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3479 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003480 if (row.isChildInGroup()) {
3481 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003482 row.setHeadsUpAnimatingAway(false);
Selim Cinekf93bf3e2018-05-08 14:43:21 -07003483 continue;
Selim Cinek76e813c2016-07-14 11:16:58 -07003484 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003485 } else {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003486 ExpandableViewState viewState = row.getViewState();
Selim Cinekeaee9c02015-06-25 11:04:20 -04003487 if (viewState == null) {
3488 // A view state was never generated for this view, so we don't need to animate
3489 // this. This may happen with notification children.
3490 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003491 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003492 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3493 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3494 // Our custom add animation
3495 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3496 } else {
3497 // Normal add animation
3498 type = AnimationEvent.ANIMATION_TYPE_ADD;
3499 }
3500 onBottom = !pinnedAndClosed;
3501 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003502 }
3503 AnimationEvent event = new AnimationEvent(row, type);
3504 event.headsUpFromBottom = onBottom;
3505 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003506 }
3507 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003508 mAddedHeadsUpChildren.clear();
3509 }
3510
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003511 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbbcebde2016-11-09 18:28:20 -08003512 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003513 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3514 return false;
3515 }
3516 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003517 }
3518
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003519 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003520 private void generateGroupExpansionEvent() {
3521 // Generate a group expansion/collapsing event if there is such a group at all
3522 if (mExpandedGroupView != null) {
3523 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3524 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3525 mExpandedGroupView = null;
3526 }
3527 }
3528
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003529 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineka5e211b2014-08-11 17:35:48 +02003530 private void generateViewResizeEvent() {
3531 if (mNeedViewResizeAnimation) {
felkachangd7835b02018-07-17 18:18:13 +08003532 boolean hasDisappearAnimation = false;
3533 for (AnimationEvent animationEvent : mAnimationEvents) {
3534 final int type = animationEvent.animationType;
3535 if (type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
Jason Monke59dc402018-08-16 12:05:01 -04003536 || type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
felkachangd7835b02018-07-17 18:18:13 +08003537 hasDisappearAnimation = true;
3538 break;
3539 }
3540 }
3541
3542 if (!hasDisappearAnimation) {
3543 mAnimationEvents.add(
3544 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3545 }
Selim Cineka5e211b2014-08-11 17:35:48 +02003546 }
3547 mNeedViewResizeAnimation = false;
3548 }
3549
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003550 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003551 private void generateChildRemovalEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003552 for (ExpandableView child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003553 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003554
3555 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003556 float removedTranslation = child.getTranslationY();
3557 boolean ignoreChildren = true;
3558 if (child instanceof ExpandableNotificationRow) {
3559 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3560 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3561 removedTranslation = row.getTranslationWhenRemoved();
3562 ignoreChildren = false;
3563 }
Selim Cinek51052042017-07-04 12:07:55 +02003564 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003565 }
Selim Cinek51052042017-07-04 12:07:55 +02003566 if (!childWasSwipedOut) {
3567 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003568 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Rohan Shaha7594962018-05-22 10:59:30 -07003569
3570 if (childWasSwipedOut && child instanceof ExpandableView) {
3571 // Clean up any potential transient views if the child has already been swiped
3572 // out, as we won't be animating it further (due to its height already being
3573 // clipped to 0.
3574 ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
3575 if (transientContainer != null) {
3576 transientContainer.removeTransientView(child);
3577 }
3578 }
Selim Cinek51052042017-07-04 12:07:55 +02003579 }
3580 int animationType = childWasSwipedOut
3581 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3582 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3583 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003584 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3585 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003586 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003587 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003588 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003589 mChildrenToRemoveAnimated.clear();
3590 }
3591
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003592 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003593 private void generatePositionChangeEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003594 for (ExpandableView child : mChildrenChangingPositions) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003595 mAnimationEvents.add(new AnimationEvent(child,
3596 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3597 }
3598 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003599 if (mGenerateChildOrderChangedEvent) {
3600 mAnimationEvents.add(new AnimationEvent(null,
3601 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3602 mGenerateChildOrderChangedEvent = false;
3603 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003604 }
3605
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003606 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003607 private void generateChildAdditionEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003608 for (ExpandableView child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003609 if (mFromMoreCardAdditions.contains(child)) {
3610 mAnimationEvents.add(new AnimationEvent(child,
3611 AnimationEvent.ANIMATION_TYPE_ADD,
3612 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3613 } else {
3614 mAnimationEvents.add(new AnimationEvent(child,
3615 AnimationEvent.ANIMATION_TYPE_ADD));
3616 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003617 }
3618 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003619 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003620 }
3621
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003622 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003623 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003624 if (mTopPaddingNeedsAnimation) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003625 AnimationEvent event;
3626 if (mAmbientState.isDark()) {
3627 event = new AnimationEvent(null /* view */,
3628 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
3629 KeyguardSliceView.DEFAULT_ANIM_DURATION);
3630 } else {
3631 event = new AnimationEvent(null /* view */,
3632 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
3633 }
3634 mAnimationEvents.add(event);
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003635 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003636 mTopPaddingNeedsAnimation = false;
3637 }
3638
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003639 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003640 private void generateActivateEvent() {
3641 if (mActivateNeedsAnimation) {
3642 mAnimationEvents.add(
3643 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3644 }
3645 mActivateNeedsAnimation = false;
3646 }
3647
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003648 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd9acca52014-09-01 22:33:25 +02003649 private void generateAnimateEverythingEvent() {
3650 if (mEverythingNeedsAnimation) {
3651 mAnimationEvents.add(
3652 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3653 }
3654 mEverythingNeedsAnimation = false;
3655 }
3656
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003657 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003658 private void generateDimmedEvent() {
3659 if (mDimmedNeedsAnimation) {
3660 mAnimationEvents.add(
3661 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3662 }
3663 mDimmedNeedsAnimation = false;
3664 }
3665
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003666 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggiae441282014-08-01 02:45:18 +02003667 private void generateHideSensitiveEvent() {
3668 if (mHideSensitiveNeedsAnimation) {
3669 mAnimationEvents.add(
3670 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3671 }
3672 mHideSensitiveNeedsAnimation = false;
3673 }
3674
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003675 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
John Spurlockbf370992014-06-17 13:58:31 -04003676 private void generateDarkEvent() {
3677 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003678 AnimationEvent ev = new AnimationEvent(null,
3679 AnimationEvent.ANIMATION_TYPE_DARK,
3680 new AnimationFilter()
3681 .animateDark()
3682 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003683 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3684 mAnimationEvents.add(ev);
John Spurlockbf370992014-06-17 13:58:31 -04003685 }
3686 mDarkNeedsAnimation = false;
3687 }
3688
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003689 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003690 private void generateGoToFullShadeEvent() {
3691 if (mGoToFullShadeNeedsAnimation) {
3692 mAnimationEvents.add(
3693 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3694 }
3695 mGoToFullShadeNeedsAnimation = false;
3696 }
3697
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003698 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
3699 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003700 return new StackScrollAlgorithm(context, this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003701 }
3702
3703 /**
3704 * @return Whether a y coordinate is inside the content.
3705 */
3706 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
3707 public boolean isInContentBounds(float y) {
3708 return y < getHeight() - getEmptyBottomMargin();
3709 }
3710
3711 @ShadeViewRefactor(RefactorComponent.INPUT)
3712 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
3713 mLongPressListener = listener;
3714 }
3715
3716 @Override
3717 @ShadeViewRefactor(RefactorComponent.INPUT)
3718 public boolean onTouchEvent(MotionEvent ev) {
3719 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
3720 || ev.getActionMasked() == MotionEvent.ACTION_UP;
3721 handleEmptySpaceClick(ev);
3722 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003723 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003724 if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
3725 if (isCancelOrUp) {
3726 mExpandHelper.onlyObserveMovements(false);
3727 }
3728 boolean wasExpandingBefore = mExpandingNotification;
3729 expandWantsIt = mExpandHelper.onTouchEvent(ev);
3730 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
3731 && !mDisallowScrollingInThisMotion) {
3732 dispatchDownEventToScroller(ev);
3733 }
3734 }
3735 boolean scrollerWantsIt = false;
3736 if (mIsExpanded && !swipingInProgress && !mExpandingNotification
3737 && !mDisallowScrollingInThisMotion) {
3738 scrollerWantsIt = onScrollTouch(ev);
3739 }
3740 boolean horizontalSwipeWantsIt = false;
3741 if (!mIsBeingDragged
3742 && !mExpandingNotification
3743 && !mExpandedInThisMotion
3744 && !mOnlyScrollingInThisMotion
3745 && !mDisallowDismissInThisMotion) {
3746 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
3747 }
3748
3749 // Check if we need to clear any snooze leavebehinds
3750 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
3751 if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
3752 && guts.getGutsContent() instanceof NotificationSnooze) {
3753 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
3754 if ((ns.isExpanded() && isCancelOrUp)
3755 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
3756 // If the leavebehind is expanded we clear it on the next up event, otherwise we
3757 // clear it on the next non-horizontal swipe or expand event.
3758 checkSnoozeLeavebehind();
3759 }
3760 }
3761 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
3762 mCheckForLeavebehind = true;
3763 }
3764 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
3765 }
3766
3767 @ShadeViewRefactor(RefactorComponent.INPUT)
3768 private void dispatchDownEventToScroller(MotionEvent ev) {
3769 MotionEvent downEvent = MotionEvent.obtain(ev);
3770 downEvent.setAction(MotionEvent.ACTION_DOWN);
3771 onScrollTouch(downEvent);
3772 downEvent.recycle();
3773 }
3774
3775 @Override
3776 @ShadeViewRefactor(RefactorComponent.INPUT)
3777 public boolean onGenericMotionEvent(MotionEvent event) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003778 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003779 || mDisallowScrollingInThisMotion) {
3780 return false;
3781 }
3782 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
3783 switch (event.getAction()) {
3784 case MotionEvent.ACTION_SCROLL: {
3785 if (!mIsBeingDragged) {
3786 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3787 if (vscroll != 0) {
3788 final int delta = (int) (vscroll * getVerticalScrollFactor());
Gus Prevas0fa58d62019-01-11 13:58:40 -05003789 if (ANCHOR_SCROLLING) {
3790 mScrollAnchorViewY -= delta;
3791 updateScrollAnchor();
3792 clampScrollPosition();
3793 updateOnScrollChange();
3794 } else {
3795 final int range = getScrollRange();
3796 int oldScrollY = mOwnScrollY;
3797 int newScrollY = oldScrollY - delta;
3798 if (newScrollY < 0) {
3799 newScrollY = 0;
3800 } else if (newScrollY > range) {
3801 newScrollY = range;
3802 }
3803 if (newScrollY != oldScrollY) {
3804 setOwnScrollY(newScrollY);
3805 return true;
3806 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003807 }
3808 }
3809 }
3810 }
3811 }
3812 }
3813 return super.onGenericMotionEvent(event);
3814 }
3815
3816 @ShadeViewRefactor(RefactorComponent.INPUT)
3817 private boolean onScrollTouch(MotionEvent ev) {
3818 if (!isScrollingEnabled()) {
3819 return false;
3820 }
3821 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
3822 return false;
3823 }
3824 mForcedScroll = null;
3825 initVelocityTrackerIfNotExists();
3826 mVelocityTracker.addMovement(ev);
3827
3828 final int action = ev.getAction();
3829
3830 switch (action & MotionEvent.ACTION_MASK) {
3831 case MotionEvent.ACTION_DOWN: {
3832 if (getChildCount() == 0 || !isInContentBounds(ev)) {
3833 return false;
3834 }
3835 boolean isBeingDragged = !mScroller.isFinished();
3836 setIsBeingDragged(isBeingDragged);
3837 /*
3838 * If being flinged and user touches, stop the fling. isFinished
3839 * will be false if being flinged.
3840 */
3841 if (!mScroller.isFinished()) {
3842 mScroller.forceFinished(true);
3843 }
3844
3845 // Remember where the motion event started
3846 mLastMotionY = (int) ev.getY();
3847 mDownX = (int) ev.getX();
3848 mActivePointerId = ev.getPointerId(0);
3849 break;
3850 }
3851 case MotionEvent.ACTION_MOVE:
3852 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
3853 if (activePointerIndex == -1) {
3854 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
3855 break;
3856 }
3857
3858 final int y = (int) ev.getY(activePointerIndex);
3859 final int x = (int) ev.getX(activePointerIndex);
3860 int deltaY = mLastMotionY - y;
3861 final int xDiff = Math.abs(x - mDownX);
3862 final int yDiff = Math.abs(deltaY);
3863 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
3864 setIsBeingDragged(true);
3865 if (deltaY > 0) {
3866 deltaY -= mTouchSlop;
3867 } else {
3868 deltaY += mTouchSlop;
3869 }
3870 }
3871 if (mIsBeingDragged) {
3872 // Scroll to follow the motion event
3873 mLastMotionY = y;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003874 float scrollAmount;
Gus Prevas0fa58d62019-01-11 13:58:40 -05003875 int range;
3876 if (ANCHOR_SCROLLING) {
3877 range = 0; // unused in the methods it's being passed to
3878 } else {
3879 range = getScrollRange();
3880 if (mExpandedInThisMotion) {
3881 range = Math.min(range, mMaxScrollAfterExpand);
3882 }
3883 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003884 if (deltaY < 0) {
3885 scrollAmount = overScrollDown(deltaY);
3886 } else {
3887 scrollAmount = overScrollUp(deltaY, range);
3888 }
3889
3890 // Calling customOverScrollBy will call onCustomOverScrolled, which
3891 // sets the scrolling if applicable.
3892 if (scrollAmount != 0.0f) {
3893 // The scrolling motion could not be compensated with the
3894 // existing overScroll, we have to scroll the view
3895 customOverScrollBy((int) scrollAmount, mOwnScrollY,
3896 range, getHeight() / 2);
3897 // If we're scrolling, leavebehinds should be dismissed
3898 checkSnoozeLeavebehind();
3899 }
3900 }
3901 break;
3902 case MotionEvent.ACTION_UP:
3903 if (mIsBeingDragged) {
3904 final VelocityTracker velocityTracker = mVelocityTracker;
3905 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
3906 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
3907
3908 if (shouldOverScrollFling(initialVelocity)) {
3909 onOverScrollFling(true, initialVelocity);
3910 } else {
3911 if (getChildCount() > 0) {
3912 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
3913 float currentOverScrollTop = getCurrentOverScrollAmount(true);
3914 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
3915 fling(-initialVelocity);
3916 } else {
3917 onOverScrollFling(false, initialVelocity);
3918 }
3919 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003920 if (ANCHOR_SCROLLING) {
3921 // TODO
3922 } else {
3923 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3924 getScrollRange())) {
3925 animateScroll();
3926 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003927 }
3928 }
3929 }
3930 }
3931 mActivePointerId = INVALID_POINTER;
3932 endDrag();
3933 }
3934
3935 break;
3936 case MotionEvent.ACTION_CANCEL:
3937 if (mIsBeingDragged && getChildCount() > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003938 if (ANCHOR_SCROLLING) {
3939 // TODO
3940 } else {
3941 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3942 getScrollRange())) {
3943 animateScroll();
3944 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003945 }
3946 mActivePointerId = INVALID_POINTER;
3947 endDrag();
3948 }
3949 break;
3950 case MotionEvent.ACTION_POINTER_DOWN: {
3951 final int index = ev.getActionIndex();
3952 mLastMotionY = (int) ev.getY(index);
3953 mDownX = (int) ev.getX(index);
3954 mActivePointerId = ev.getPointerId(index);
3955 break;
3956 }
3957 case MotionEvent.ACTION_POINTER_UP:
3958 onSecondaryPointerUp(ev);
3959 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
3960 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
3961 break;
3962 }
3963 return true;
3964 }
3965
3966 @ShadeViewRefactor(RefactorComponent.INPUT)
3967 protected boolean isInsideQsContainer(MotionEvent ev) {
3968 return ev.getY() < mQsContainer.getBottom();
3969 }
3970
3971 @ShadeViewRefactor(RefactorComponent.INPUT)
3972 private void onOverScrollFling(boolean open, int initialVelocity) {
3973 if (mOverscrollTopChangedListener != null) {
3974 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
3975 }
3976 mDontReportNextOverScroll = true;
3977 setOverScrollAmount(0.0f, true, false);
3978 }
3979
3980
3981 @ShadeViewRefactor(RefactorComponent.INPUT)
3982 private void onSecondaryPointerUp(MotionEvent ev) {
3983 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
3984 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
3985 final int pointerId = ev.getPointerId(pointerIndex);
3986 if (pointerId == mActivePointerId) {
3987 // This was our active pointer going up. Choose a new
3988 // active pointer and adjust accordingly.
3989 // TODO: Make this decision more intelligent.
3990 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
3991 mLastMotionY = (int) ev.getY(newPointerIndex);
3992 mActivePointerId = ev.getPointerId(newPointerIndex);
3993 if (mVelocityTracker != null) {
3994 mVelocityTracker.clear();
3995 }
3996 }
3997 }
3998
3999 @ShadeViewRefactor(RefactorComponent.INPUT)
4000 private void endDrag() {
4001 setIsBeingDragged(false);
4002
4003 recycleVelocityTracker();
4004
4005 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
4006 setOverScrollAmount(0, true /* onTop */, true /* animate */);
4007 }
4008 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
4009 setOverScrollAmount(0, false /* onTop */, true /* animate */);
4010 }
4011 }
4012
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004013 @Override
4014 @ShadeViewRefactor(RefactorComponent.INPUT)
4015 public boolean onInterceptTouchEvent(MotionEvent ev) {
4016 initDownStates(ev);
4017 handleEmptySpaceClick(ev);
4018 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05004019 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004020 if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
4021 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
4022 }
4023 boolean scrollWantsIt = false;
4024 if (!swipingInProgress && !mExpandingNotification) {
4025 scrollWantsIt = onInterceptTouchEventScroll(ev);
4026 }
4027 boolean swipeWantsIt = false;
4028 if (!mIsBeingDragged
4029 && !mExpandingNotification
4030 && !mExpandedInThisMotion
4031 && !mOnlyScrollingInThisMotion
4032 && !mDisallowDismissInThisMotion) {
4033 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
4034 }
4035 // Check if we need to clear any snooze leavebehinds
4036 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
4037 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
4038 if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
4039 !expandWantsIt && !scrollWantsIt) {
4040 mCheckForLeavebehind = false;
4041 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
4042 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4043 false /* resetMenu */);
4044 }
4045 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
4046 mCheckForLeavebehind = true;
4047 }
4048 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
4049 }
4050
4051 @ShadeViewRefactor(RefactorComponent.INPUT)
4052 private void handleEmptySpaceClick(MotionEvent ev) {
4053 switch (ev.getActionMasked()) {
4054 case MotionEvent.ACTION_MOVE:
4055 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
4056 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
4057 mTouchIsClick = false;
4058 }
4059 break;
4060 case MotionEvent.ACTION_UP:
4061 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
4062 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
4063 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
4064 }
4065 break;
4066 }
4067 }
4068
4069 @ShadeViewRefactor(RefactorComponent.INPUT)
4070 private void initDownStates(MotionEvent ev) {
4071 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
4072 mExpandedInThisMotion = false;
4073 mOnlyScrollingInThisMotion = !mScroller.isFinished();
4074 mDisallowScrollingInThisMotion = false;
4075 mDisallowDismissInThisMotion = false;
4076 mTouchIsClick = true;
4077 mInitialTouchX = ev.getX();
4078 mInitialTouchY = ev.getY();
4079 }
4080 }
4081
4082 @Override
4083 @ShadeViewRefactor(RefactorComponent.INPUT)
4084 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
4085 super.requestDisallowInterceptTouchEvent(disallowIntercept);
4086 if (disallowIntercept) {
4087 cancelLongPress();
4088 }
4089 }
4090
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004091 @ShadeViewRefactor(RefactorComponent.INPUT)
Selim Cinek67b22602014-03-10 15:40:16 +01004092 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02004093 if (!isScrollingEnabled()) {
4094 return false;
4095 }
Selim Cinek67b22602014-03-10 15:40:16 +01004096 /*
4097 * This method JUST determines whether we want to intercept the motion.
4098 * If we return true, onMotionEvent will be called and we do the actual
4099 * scrolling there.
4100 */
4101
4102 /*
Jason Monke59dc402018-08-16 12:05:01 -04004103 * Shortcut the most recurring case: the user is in the dragging
4104 * state and is moving their finger. We want to intercept this
4105 * motion.
4106 */
Selim Cinek67b22602014-03-10 15:40:16 +01004107 final int action = ev.getAction();
4108 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
4109 return true;
4110 }
4111
Selim Cinek67b22602014-03-10 15:40:16 +01004112 switch (action & MotionEvent.ACTION_MASK) {
4113 case MotionEvent.ACTION_MOVE: {
4114 /*
4115 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04004116 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01004117 */
4118
4119 /*
Jason Monke59dc402018-08-16 12:05:01 -04004120 * Locally do absolute value. mLastMotionY is set to the y value
4121 * of the down event.
4122 */
Selim Cinek67b22602014-03-10 15:40:16 +01004123 final int activePointerId = mActivePointerId;
4124 if (activePointerId == INVALID_POINTER) {
4125 // If we don't have a valid id, the touch down wasn't on content.
4126 break;
4127 }
4128
4129 final int pointerIndex = ev.findPointerIndex(activePointerId);
4130 if (pointerIndex == -1) {
4131 Log.e(TAG, "Invalid pointerId=" + activePointerId
4132 + " in onInterceptTouchEvent");
4133 break;
4134 }
4135
4136 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02004137 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01004138 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02004139 final int xDiff = Math.abs(x - mDownX);
4140 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01004141 setIsBeingDragged(true);
4142 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004143 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01004144 initVelocityTrackerIfNotExists();
4145 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01004146 }
4147 break;
4148 }
4149
4150 case MotionEvent.ACTION_DOWN: {
4151 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05304152 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07004153 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01004154 setIsBeingDragged(false);
4155 recycleVelocityTracker();
4156 break;
4157 }
4158
4159 /*
4160 * Remember location of down touch.
4161 * ACTION_DOWN always refers to pointer index 0.
4162 */
4163 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004164 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01004165 mActivePointerId = ev.getPointerId(0);
4166
4167 initOrResetVelocityTracker();
4168 mVelocityTracker.addMovement(ev);
4169 /*
Jason Monke59dc402018-08-16 12:05:01 -04004170 * If being flinged and user touches the screen, initiate drag;
4171 * otherwise don't. mScroller.isFinished should be false when
4172 * being flinged.
4173 */
Selim Cinek67b22602014-03-10 15:40:16 +01004174 boolean isBeingDragged = !mScroller.isFinished();
4175 setIsBeingDragged(isBeingDragged);
4176 break;
4177 }
4178
4179 case MotionEvent.ACTION_CANCEL:
4180 case MotionEvent.ACTION_UP:
4181 /* Release the drag */
4182 setIsBeingDragged(false);
4183 mActivePointerId = INVALID_POINTER;
4184 recycleVelocityTracker();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004185 if (ANCHOR_SCROLLING) {
4186 // TODO
4187 } else {
4188 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
4189 animateScroll();
4190 }
Selim Cinek67b22602014-03-10 15:40:16 +01004191 }
4192 break;
4193 case MotionEvent.ACTION_POINTER_UP:
4194 onSecondaryPointerUp(ev);
4195 break;
4196 }
4197
4198 /*
Jason Monke59dc402018-08-16 12:05:01 -04004199 * The only time we want to intercept motion events is if we are in the
4200 * drag mode.
4201 */
Selim Cinek67b22602014-03-10 15:40:16 +01004202 return mIsBeingDragged;
4203 }
4204
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004205 /**
4206 * @return Whether the specified motion event is actually happening over the content.
4207 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004208 @ShadeViewRefactor(RefactorComponent.INPUT)
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004209 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01004210 return isInContentBounds(event.getY());
4211 }
4212
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004213
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004214 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004215 @ShadeViewRefactor(RefactorComponent.INPUT)
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004216 void setIsBeingDragged(boolean isDragged) {
Selim Cinek67b22602014-03-10 15:40:16 +01004217 mIsBeingDragged = isDragged;
4218 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004219 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004220 cancelLongPress();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004221 resetExposedMenuView(true /* animate */, true /* force */);
Selim Cinek67b22602014-03-10 15:40:16 +01004222 }
4223 }
4224
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004225 @ShadeViewRefactor(RefactorComponent.INPUT)
4226 public void requestDisallowLongPress() {
4227 cancelLongPress();
4228 }
4229
4230 @ShadeViewRefactor(RefactorComponent.INPUT)
4231 public void requestDisallowDismiss() {
4232 mDisallowDismissInThisMotion = true;
4233 }
4234
4235 @ShadeViewRefactor(RefactorComponent.INPUT)
4236 public void cancelLongPress() {
4237 mSwipeHelper.cancelLongPress();
4238 }
4239
4240 @ShadeViewRefactor(RefactorComponent.INPUT)
4241 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
4242 mOnEmptySpaceClickListener = listener;
4243 }
4244
4245 /** @hide */
4246 @Override
4247 @ShadeViewRefactor(RefactorComponent.INPUT)
4248 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4249 if (super.performAccessibilityActionInternal(action, arguments)) {
4250 return true;
4251 }
4252 if (!isEnabled()) {
4253 return false;
4254 }
4255 int direction = -1;
4256 switch (action) {
4257 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4258 // fall through
4259 case android.R.id.accessibilityActionScrollDown:
4260 direction = 1;
4261 // fall through
4262 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4263 // fall through
4264 case android.R.id.accessibilityActionScrollUp:
Gus Prevas0fa58d62019-01-11 13:58:40 -05004265 if (ANCHOR_SCROLLING) {
4266 // TODO
4267 } else {
4268 final int viewportHeight =
4269 getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
4270 - mShelf.getIntrinsicHeight();
4271 final int targetScrollY = Math.max(0,
4272 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4273 if (targetScrollY != mOwnScrollY) {
4274 mScroller.startScroll(mScrollX, mOwnScrollY, 0,
4275 targetScrollY - mOwnScrollY);
4276 animateScroll();
4277 return true;
4278 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004279 }
4280 break;
4281 }
4282 return false;
4283 }
4284
4285 @ShadeViewRefactor(RefactorComponent.INPUT)
4286 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4287 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
4288 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
4289 View translatingParentView = mSwipeHelper.getTranslatingParentView();
4290 View view = null;
4291 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4292 // Only close visible guts if they're not a leavebehind.
4293 view = guts;
4294 } else if (menuRow != null && menuRow.isMenuVisible()
4295 && translatingParentView != null) {
4296 // Checking menu
4297 view = translatingParentView;
4298 }
4299 if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
4300 // Touch was outside visible guts / menu notification, close what's visible
4301 mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
4302 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4303 false /* resetMenu */);
4304 resetExposedMenuView(true /* animate */, true /* force */);
4305 }
4306 }
4307
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05004308 @ShadeViewRefactor(RefactorComponent.INPUT)
4309 private void setSwipingInProgress(boolean swiping) {
4310 mSwipingInProgress = swiping;
4311 if (swiping) {
4312 requestDisallowInterceptTouchEvent(true);
4313 }
4314 }
4315
Selim Cinek67b22602014-03-10 15:40:16 +01004316 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004317 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01004318 public void onWindowFocusChanged(boolean hasWindowFocus) {
4319 super.onWindowFocusChanged(hasWindowFocus);
4320 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004321 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01004322 }
4323 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004324
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07004325 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004326 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04004327 public void clearChildFocus(View child) {
4328 super.clearChildFocus(child);
4329 if (mForcedScroll == child) {
4330 mForcedScroll = null;
4331 }
4332 }
4333
Selim Cinekfab078b2014-03-27 22:45:58 +01004334 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004335 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfab078b2014-03-27 22:45:58 +01004336 public boolean isScrolledToTop() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004337 if (ANCHOR_SCROLLING) {
4338 updateScrollAnchor();
4339 // TODO: once we're recycling this will need to check the adapter position of the child
4340 return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0;
4341 } else {
4342 return mOwnScrollY == 0;
4343 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004344 }
4345
4346 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004347 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004348 public boolean isScrolledToBottom() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004349 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05004350 return getMaxPositiveScrollAmount() <= 0;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004351 } else {
4352 return mOwnScrollY >= getScrollRange();
4353 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004354 }
4355
4356 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004357 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfab078b2014-03-27 22:45:58 +01004358 public View getHostView() {
4359 return this;
4360 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004361
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004362 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004363 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08004364 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004365 }
4366
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004367 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004368 public void checkSnoozeLeavebehind() {
4369 if (mCheckForLeavebehind) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04004370 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
Eliot Courtney47098cb2017-10-18 17:30:30 +09004371 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4372 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004373 mCheckForLeavebehind = false;
4374 }
4375 }
4376
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004377 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004378 public void resetCheckSnoozeLeavebehind() {
4379 mCheckForLeavebehind = true;
4380 }
4381
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004382 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004383 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004384 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004385 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004386 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02004387 }
4388
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004389 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004390 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004391 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004392 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08004393 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02004394 if (!mIsExpanded) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004395 resetScrollPosition();
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004396 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004397 clearTemporaryViews();
4398 clearUserLockedViews();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004399 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08004400 if (draggedViews.size() > 0) {
4401 draggedViews.clear();
4402 updateContinuousShadowDrawing();
4403 }
Selim Cinek5b1591a2017-07-03 17:05:01 +02004404 }
4405 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01004406
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004407 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004408 private void clearUserLockedViews() {
4409 for (int i = 0; i < getChildCount(); i++) {
4410 ExpandableView child = (ExpandableView) getChildAt(i);
4411 if (child instanceof ExpandableNotificationRow) {
4412 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4413 row.setUserLocked(false);
4414 }
4415 }
4416 }
4417
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004418 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004419 private void clearTemporaryViews() {
Selim Cinek9dd0d042018-05-14 18:12:42 -07004420 // lets make sure nothing is transient anymore
Rohan Shah8ee53652018-04-05 11:13:50 -07004421 clearTemporaryViewsInGroup(this);
Selim Cinek5b1591a2017-07-03 17:05:01 +02004422 for (int i = 0; i < getChildCount(); i++) {
4423 ExpandableView child = (ExpandableView) getChildAt(i);
4424 if (child instanceof ExpandableNotificationRow) {
4425 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Rohan Shah8ee53652018-04-05 11:13:50 -07004426 clearTemporaryViewsInGroup(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07004427 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02004428 }
Selim Cinek1685e632014-04-08 02:27:49 +02004429 }
4430
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004431 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shah8ee53652018-04-05 11:13:50 -07004432 private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07004433 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04004434 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07004435 }
4436 }
4437
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004438 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004439 public void onPanelTrackingStarted() {
4440 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004441 mAmbientState.setPanelTracking(true);
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004442 resetExposedMenuView(true /* animate */, true /* force */);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004443 }
Jason Monke59dc402018-08-16 12:05:01 -04004444
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004445 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004446 public void onPanelTrackingStopped() {
4447 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004448 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004449 }
4450
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004451 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb24e0a92015-06-09 20:17:30 -07004452 public void resetScrollPosition() {
4453 mScroller.abortAnimation();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004454 if (ANCHOR_SCROLLING) {
4455 // TODO: once we're recycling this will need to modify the adapter position instead
4456 mScrollAnchorView = getFirstChildNotGone();
4457 mScrollAnchorViewY = 0;
4458 updateOnScrollChange();
4459 } else {
4460 setOwnScrollY(0);
4461 }
Selim Cinekb24e0a92015-06-09 20:17:30 -07004462 }
4463
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004464 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04004465 public void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02004466 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02004467 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02004468 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02004469 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08004470 if (!mIsExpanded) {
4471 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004472 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08004473 }
Selim Cinekcab4a602014-09-03 14:47:57 +02004474 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02004475 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01004476 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02004477 }
4478 }
4479
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004480 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek98713a42015-09-21 15:47:20 +02004481 private void updateChronometers() {
4482 int childCount = getChildCount();
4483 for (int i = 0; i < childCount; i++) {
4484 updateChronometerForChild(getChildAt(i));
4485 }
4486 }
4487
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004488 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek98713a42015-09-21 15:47:20 +02004489 private void updateChronometerForChild(View child) {
4490 if (child instanceof ExpandableNotificationRow) {
4491 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4492 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02004493 }
Selim Cinek1685e632014-04-08 02:27:49 +02004494 }
4495
Jorim Jaggibe565df2014-04-28 17:51:23 +02004496 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004497 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004498 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02004499 updateScrollPositionOnExpandInBottom(view);
4500 clampScrollPosition();
Lucas Dupin60661a62018-04-12 10:50:13 -07004501 notifyHeightChangeListener(view, needsAnimation);
Selim Cinekbc243a92016-09-27 16:35:13 -07004502 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
4503 ? (ExpandableNotificationRow) view
4504 : null;
Gus Prevase2d6f042018-10-17 15:25:30 -04004505 NotificationSection firstSection = getFirstVisibleSection();
4506 ActivatableNotificationView firstVisibleChild =
4507 firstSection == null ? null : firstSection.getFirstVisibleChild();
4508 if (row != null) {
4509 if (row == firstVisibleChild
4510 || row.getNotificationParent() == firstVisibleChild) {
4511 updateAlgorithmLayoutMinHeight();
4512 }
Selim Cinekbc243a92016-09-27 16:35:13 -07004513 }
Selim Cinekb5605e52015-02-20 18:21:41 +01004514 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08004515 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01004516 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004517 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02004518 }
4519
Selim Cineka5e211b2014-08-11 17:35:48 +02004520 @Override
4521 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02004522 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02004523 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02004524 }
4525
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004526 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf7a14c02014-07-07 14:01:46 +02004527 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07004528 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02004529 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004530 // TODO: once we're recycling this will need to check the adapter position of the child
Selim Cinekcb9400a2015-06-03 16:56:13 +02004531 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08004532 if (row.isSummaryWithChildren()) {
4533 return;
4534 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02004535 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08004536 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07004537 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08004538 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07004539 }
Selim Cinekdb167372016-11-17 15:41:17 -08004540 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Gus Prevase2d6f042018-10-17 15:25:30 -04004541 NotificationSection lastSection = getLastVisibleSection();
4542 ActivatableNotificationView lastVisibleChild =
4543 lastSection == null ? null : lastSection.getLastVisibleChild();
4544 if (row != lastVisibleChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08004545 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
4546 }
4547 if (endPosition > layoutEnd) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004548 if (ANCHOR_SCROLLING) {
4549 mScrollAnchorViewY -= (endPosition - layoutEnd);
4550 updateScrollAnchor();
4551 updateOnScrollChange();
4552 } else {
4553 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
4554 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02004555 mDisallowScrollingInThisMotion = true;
4556 }
4557 }
4558 }
4559 }
4560
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004561 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggibe565df2014-04-28 17:51:23 +02004562 public void setOnHeightChangedListener(
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004563 ExpandableView.OnHeightChangedListener onHeightChangedListener) {
4564 this.mOnHeightChangedListener = onHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004565 }
4566
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004567 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02004568 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08004569 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02004570 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07004571 runAnimationFinishedRunnables();
Selim Cinek9dd0d042018-05-14 18:12:42 -07004572 clearTransient();
Selim Cinek8fc78752016-07-13 14:34:56 -07004573 clearHeadsUpDisappearRunning();
4574 }
4575
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004576 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8fc78752016-07-13 14:34:56 -07004577 private void clearHeadsUpDisappearRunning() {
4578 for (int i = 0; i < getChildCount(); i++) {
4579 View view = getChildAt(i);
4580 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07004581 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07004582 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07004583 if (row.isSummaryWithChildren()) {
4584 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004585 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07004586 }
4587 }
Selim Cinek8fc78752016-07-13 14:34:56 -07004588 }
4589 }
Selim Cinek0fccc722015-07-29 17:04:36 -07004590 }
4591
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004592 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9dd0d042018-05-14 18:12:42 -07004593 private void clearTransient() {
4594 for (ExpandableView view : mClearTransientViewsWhenFinished) {
4595 StackStateAnimator.removeTransientView(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07004596 }
Selim Cinek9dd0d042018-05-14 18:12:42 -07004597 mClearTransientViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07004598 }
4599
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004600 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek32a59fd32015-06-10 13:54:42 -07004601 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004602 for (Runnable runnable : mAnimationFinishedRunnables) {
4603 runnable.run();
4604 }
4605 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02004606 }
4607
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004608 /**
4609 * See {@link AmbientState#setDimmed}.
4610 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004611 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004612 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07004613 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004614 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02004615 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004616 mDimmedNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004617 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08004618 animateDimmed(dimmed);
4619 } else {
4620 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004621 }
4622 requestChildrenUpdate();
4623 }
4624
Selim Cinek8a9308d2017-08-24 09:31:08 -07004625 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004626 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8a9308d2017-08-24 09:31:08 -07004627 boolean isDimmed() {
4628 return mAmbientState.isDimmed();
4629 }
4630
Gus Prevase2d6f042018-10-17 15:25:30 -04004631 @VisibleForTesting
4632 int getSectionBoundaryIndex(int boundaryNum) {
4633 return mAmbientState.getSectionBoundaryIndex(boundaryNum);
4634 }
4635
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004636 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd35c2792016-01-21 13:20:57 -08004637 private void setDimAmount(float dimAmount) {
4638 mDimAmount = dimAmount;
4639 updateBackgroundDimming();
4640 }
4641
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004642 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd35c2792016-01-21 13:20:57 -08004643 private void animateDimmed(boolean dimmed) {
4644 if (mDimAnimator != null) {
4645 mDimAnimator.cancel();
4646 }
4647 float target = dimmed ? 1.0f : 0.0f;
4648 if (target == mDimAmount) {
4649 return;
4650 }
4651 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
4652 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
4653 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
4654 mDimAnimator.addListener(mDimEndListener);
4655 mDimAnimator.addUpdateListener(mDimUpdateListener);
4656 mDimAnimator.start();
4657 }
Evan Laird91d0f102018-09-18 17:39:55 -04004658
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004659 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Evan Laird91d0f102018-09-18 17:39:55 -04004660 private void setHideSensitive(boolean hideSensitive, boolean animate) {
Jorim Jaggiae441282014-08-01 02:45:18 +02004661 if (hideSensitive != mAmbientState.isHideSensitive()) {
4662 int childCount = getChildCount();
4663 for (int i = 0; i < childCount; i++) {
4664 ExpandableView v = (ExpandableView) getChildAt(i);
4665 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
4666 }
4667 mAmbientState.setHideSensitive(hideSensitive);
4668 if (animate && mAnimationsEnabled) {
4669 mHideSensitiveNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004670 mNeedsAnimation = true;
Jorim Jaggiae441282014-08-01 02:45:18 +02004671 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08004672 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02004673 requestChildrenUpdate();
4674 }
4675 }
4676
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004677 /**
4678 * See {@link AmbientState#setActivatedChild}.
4679 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004680 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka32ab602014-06-11 15:06:01 +02004681 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004682 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02004683 if (mAnimationsEnabled) {
4684 mActivateNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004685 mNeedsAnimation = true;
Jorim Jaggi75c95042014-05-16 19:09:59 +02004686 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004687 requestChildrenUpdate();
4688 }
4689
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004690 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka32ab602014-06-11 15:06:01 +02004691 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004692 return mAmbientState.getActivatedChild();
4693 }
4694
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004695 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02004696 private void applyCurrentState() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004697 int numChildren = getChildCount();
4698 for (int i = 0; i < numChildren; i++) {
4699 ExpandableView child = (ExpandableView) getChildAt(i);
4700 child.applyViewState();
4701 }
4702
Selim Cinekf4c19962014-05-01 21:55:31 +02004703 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004704 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02004705 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07004706 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07004707 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08004708 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08004709 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08004710 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08004711 }
4712
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004713 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08004714 private void updateViewShadows() {
4715 // we need to work around an issue where the shadow would not cast between siblings when
4716 // their z difference is between 0 and 0.1
4717
4718 // Lefts first sort by Z difference
4719 for (int i = 0; i < getChildCount(); i++) {
4720 ExpandableView child = (ExpandableView) getChildAt(i);
4721 if (child.getVisibility() != GONE) {
4722 mTmpSortedChildren.add(child);
4723 }
4724 }
4725 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
4726
4727 // Now lets update the shadow for the views
4728 ExpandableView previous = null;
4729 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
4730 ExpandableView expandableView = mTmpSortedChildren.get(i);
4731 float translationZ = expandableView.getTranslationZ();
4732 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
4733 float diff = otherZ - translationZ;
4734 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
4735 // There is no fake shadow to be drawn
4736 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
4737 } else {
4738 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07004739 expandableView.getTranslationY() - previous.getExtraBottomPadding();
4740 expandableView.setFakeShadowIntensity(
4741 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08004742 previous.getOutlineAlpha(), (int) yLocation,
4743 previous.getOutlineTranslation());
4744 }
4745 previous = expandableView;
4746 }
4747
4748 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02004749 }
4750
Lucas Dupine17ce522017-07-17 15:45:06 -07004751 /**
4752 * Update colors of "dismiss" and "empty shade" views.
4753 *
4754 * @param lightTheme True if light theme should be used.
4755 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004756 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupine17ce522017-07-17 15:45:06 -07004757 public void updateDecorViews(boolean lightTheme) {
4758 if (lightTheme == mUsingLightTheme) {
4759 return;
4760 }
4761 mUsingLightTheme = lightTheme;
4762 Context context = new ContextThemeWrapper(mContext,
4763 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
Jason Changb4e879d2018-04-11 11:17:58 +08004764 final int textColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004765 mFooterView.setTextColor(textColor);
Lucas Dupine17ce522017-07-17 15:45:06 -07004766 mEmptyShadeView.setTextColor(textColor);
4767 }
4768
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004769 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004770 public void goToFullShade(long delay) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004771 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004772 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004773 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004774 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02004775 }
4776
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004777 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02004778 public void cancelExpandHelper() {
4779 mExpandHelper.cancel();
4780 }
4781
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004782 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02004783 public void setIntrinsicPadding(int intrinsicPadding) {
4784 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07004785 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02004786 }
4787
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004788 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02004789 public int getIntrinsicPadding() {
4790 return mIntrinsicPadding;
4791 }
4792
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004793 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02004794 * @return the y position of the first notification
4795 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004796 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi457cc352014-06-02 22:47:42 +02004797 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07004798 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02004799 }
4800
Selim Cinekc0ce82d2014-06-10 13:21:15 +02004801 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004802 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc0ce82d2014-06-10 13:21:15 +02004803 public boolean shouldDelayChildPressedState() {
4804 return true;
4805 }
4806
Jorim Jaggi457cc352014-06-02 22:47:42 +02004807 /**
John Spurlockbf370992014-06-17 13:58:31 -04004808 * See {@link AmbientState#setDark}.
4809 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004810 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004811 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02004812 if (mAmbientState.isDark() == dark) {
4813 return;
4814 }
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004815 if (!dark) {
4816 mShowDarkShelf = false;
4817 }
John Spurlockbf370992014-06-17 13:58:31 -04004818 mAmbientState.setDark(dark);
4819 if (animate && mAnimationsEnabled) {
4820 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004821 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004822 mNeedsAnimation = true;
Selim Cinek6811d722016-01-19 17:53:12 -08004823 } else {
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004824 setDarkAmount(dark ? 1f : 0f);
Selim Cinek6811d722016-01-19 17:53:12 -08004825 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08004826 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004827 requestChildrenUpdate();
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004828 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004829 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04004830 }
4831
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004832 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Bill Line60aa1e2018-06-13 18:07:15 +08004833 private void updatePanelTranslation() {
Lucas Dupinb46d0a22019-01-11 16:57:16 -08004834 setTranslationX(mHorizontalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
Bill Line60aa1e2018-06-13 18:07:15 +08004835 }
4836
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004837 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupinb46d0a22019-01-11 16:57:16 -08004838 public void setHorizontalPanelTranslation(float verticalPanelTranslation) {
4839 mHorizontalPanelTranslation = verticalPanelTranslation;
Bill Line60aa1e2018-06-13 18:07:15 +08004840 updatePanelTranslation();
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004841 }
4842
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004843 /**
4844 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
4845 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
4846 * {@link #mAmbientState}'s dark mode is toggled.
4847 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004848 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004849 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08004850 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07004851 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004852 }
4853
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004854 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupind285cf02018-01-18 09:18:23 -08004855 private void setDarkAmount(float darkAmount) {
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004856 setDarkAmount(darkAmount, darkAmount);
4857 }
4858
4859 /**
4860 * Sets the current dark amount.
4861 *
Jason Monke59dc402018-08-16 12:05:01 -04004862 * @param linearDarkAmount The dark amount that follows linear interpoloation in the
4863 * animation,
4864 * i.e. animates from 0 to 1 or vice-versa in a linear manner.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004865 * @param interpolatedDarkAmount The dark amount that follows the actual interpolation of the
Jason Monke59dc402018-08-16 12:05:01 -04004866 * animation curve.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004867 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004868 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004869 public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
4870 mLinearDarkAmount = linearDarkAmount;
4871 mInterpolatedDarkAmount = interpolatedDarkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -07004872 boolean wasFullyDark = mAmbientState.isFullyDark();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004873 boolean wasDarkAtAll = mAmbientState.isDarkAtAll();
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004874 mAmbientState.setDarkAmount(interpolatedDarkAmount);
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004875 boolean nowFullyDark = mAmbientState.isFullyDark();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004876 boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004877 if (nowFullyDark != wasFullyDark) {
Lucas Dupin16cfe452018-02-08 13:14:50 -08004878 updateContentHeight();
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004879 if (nowFullyDark && mShowDarkShelf) {
4880 updateDarkShelfVisibility();
4881 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08004882 }
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004883 if (!wasDarkAtAll && nowDarkAtAll) {
4884 resetExposedMenuView(true /* animate */, true /* animate */);
4885 }
Lucas Dupin60661a62018-04-12 10:50:13 -07004886 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07004887 updateBackgroundDimming();
Bill Line60aa1e2018-06-13 18:07:15 +08004888 updatePanelTranslation();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004889 requestChildrenUpdate();
Selim Cinek972123d2016-05-03 14:25:58 -07004890 }
4891
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004892 /**
4893 * If the shelf should be visible when the device is in ambient mode (dozing.)
4894 */
4895 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
4896 public void setShowDarkShelf(boolean showDarkShelf) {
4897 mShowDarkShelf = showDarkShelf;
4898 }
4899
4900 private void updateDarkShelfVisibility() {
4901 DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
4902 if (dozeParameters.shouldControlScreenOff()) {
4903 mShelf.fadeInTranslating();
4904 }
4905 updateClipping();
4906 }
4907
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004908 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004909 public void notifyDarkAnimationStart(boolean dark) {
4910 // We only swap the scaling factor if we're fully dark or fully awake to avoid
4911 // interpolation issues when playing with the power button.
4912 if (mInterpolatedDarkAmount == 0 || mInterpolatedDarkAmount == 1) {
4913 mBackgroundXFactor = dark ? 1.8f : 1.5f;
4914 mDarkXInterpolator = dark
4915 ? Interpolators.FAST_OUT_SLOW_IN_REVERSE
4916 : Interpolators.FAST_OUT_SLOW_IN;
4917 }
Selim Cinek972123d2016-05-03 14:25:58 -07004918 }
4919
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004920 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004921 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07004922 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004923 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4924 }
4925 if (screenLocation.y > getBottomMostNotificationBottom()) {
4926 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
4927 }
4928 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
4929 if (child != null) {
4930 return getNotGoneIndex(child);
4931 } else {
4932 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4933 }
4934 }
4935
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004936 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004937 private int getNotGoneIndex(View child) {
4938 int count = getChildCount();
4939 int notGoneIndex = 0;
4940 for (int i = 0; i < count; i++) {
4941 View v = getChildAt(i);
4942 if (child == v) {
4943 return notGoneIndex;
4944 }
4945 if (v.getVisibility() != View.GONE) {
4946 notGoneIndex++;
4947 }
4948 }
4949 return -1;
4950 }
4951
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004952 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004953 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004954 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004955 if (mFooterView != null) {
4956 index = indexOfChild(mFooterView);
4957 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004958 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004959 mFooterView = footerView;
4960 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004961 }
4962
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004963 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004964 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004965 int index = -1;
4966 if (mEmptyShadeView != null) {
4967 index = indexOfChild(mEmptyShadeView);
4968 removeView(mEmptyShadeView);
4969 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004970 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004971 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004972 }
4973
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004974 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004975 public void updateEmptyShadeView(boolean visible) {
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004976 mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
Lucas Dupinc9274ff2018-05-09 17:40:20 -07004977
4978 int oldTextRes = mEmptyShadeView.getTextResource();
4979 int newTextRes = mStatusBar.areNotificationsHidden()
4980 ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
4981 if (oldTextRes != newTextRes) {
4982 mEmptyShadeView.setText(newTextRes);
4983 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004984 }
4985
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004986 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004987 public void updateFooterView(boolean visible, boolean showDismissView) {
4988 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004989 return;
4990 }
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004991 boolean animate = mIsExpanded && mAnimationsEnabled;
4992 mFooterView.setVisible(visible, animate);
4993 mFooterView.setSecondaryVisible(showDismissView, animate);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004994 }
4995
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004996 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dan Sandlereceda3d2014-07-21 15:35:01 -04004997 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4998 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004999 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07005000 handleDismissAllClipping();
5001 }
5002
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005003 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek9c17b772015-07-07 20:37:09 -07005004 private void handleDismissAllClipping() {
5005 final int count = getChildCount();
5006 boolean previousChildWillBeDismissed = false;
5007 for (int i = 0; i < count; i++) {
5008 ExpandableView child = (ExpandableView) getChildAt(i);
5009 if (child.getVisibility() == GONE) {
5010 continue;
5011 }
5012 if (mDismissAllInProgress && previousChildWillBeDismissed) {
5013 child.setMinClipTopAmount(child.getClipTopAmount());
5014 } else {
5015 child.setMinClipTopAmount(0);
5016 }
5017 previousChildWillBeDismissed = canChildBeDismissed(child);
5018 }
Selim Cineka272dfe2015-02-20 18:12:28 +01005019 }
5020
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005021 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04005022 public boolean isFooterViewNotGone() {
5023 return mFooterView != null
5024 && mFooterView.getVisibility() != View.GONE
5025 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02005026 }
5027
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005028 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek38475442018-05-18 11:11:46 -07005029 public boolean isFooterViewContentVisible() {
5030 return mFooterView != null && mFooterView.isContentVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02005031 }
5032
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005033 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04005034 public int getFooterViewHeight() {
5035 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02005036 }
5037
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005038 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01005039 public int getEmptyShadeViewHeight() {
5040 return mEmptyShadeView.getHeight();
5041 }
5042
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005043 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggie0640dd2014-08-05 23:12:40 +02005044 public float getBottomMostNotificationBottom() {
5045 final int count = getChildCount();
5046 float max = 0;
5047 for (int childIdx = 0; childIdx < count; childIdx++) {
5048 ExpandableView child = (ExpandableView) getChildAt(childIdx);
5049 if (child.getVisibility() == GONE) {
5050 continue;
5051 }
Selim Cineka686b2c2016-10-26 13:58:27 -07005052 float bottom = child.getTranslationY() + child.getActualHeight()
5053 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02005054 if (bottom > max) {
5055 max = bottom;
5056 }
5057 }
Selim Cinekd2281152015-04-10 14:37:46 -07005058 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02005059 }
5060
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005061 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk2a6ea9c2017-01-26 11:14:51 -05005062 public void setStatusBar(StatusBar statusBar) {
5063 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02005064 }
5065
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005066 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb5605e52015-02-20 18:21:41 +01005067 public void setGroupManager(NotificationGroupManager groupManager) {
5068 this.mGroupManager = groupManager;
Kevin01a53cb2018-11-09 18:19:54 -08005069 mGroupManager.addOnGroupChangeListener(mOnGroupChangeListener);
Selim Cinek379ff8f2015-02-20 17:03:16 +01005070 }
5071
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005072 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek379ff8f2015-02-20 17:03:16 +01005073 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02005074 if (mIsExpanded && mAnimationsEnabled) {
5075 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01005076 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02005077 requestChildrenUpdate();
5078 }
5079 }
5080
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005081 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek04fb2582015-06-02 19:58:09 +02005082 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01005083 int childCount = getChildCount();
5084 for (int i = childCount - 1; i >= 0; i--) {
5085 ExpandableView child = (ExpandableView) getChildAt(i);
5086 if (child.getVisibility() != View.GONE) {
5087 float childTop = child.getY();
5088 if (childTop > touchY) {
5089 // we are above a notification entirely let's abort
5090 return false;
5091 }
Selim Cineka686b2c2016-10-26 13:58:27 -07005092 boolean belowChild = touchY > childTop + child.getActualHeight()
5093 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04005094 if (child == mFooterView) {
Jason Monke59dc402018-08-16 12:05:01 -04005095 if (!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
5096 touchY - childTop)) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01005097 // We clicked on the dismiss button
5098 return false;
5099 }
5100 } else if (child == mEmptyShadeView) {
5101 // We arrived at the empty shade view, for which we accept all clicks
5102 return true;
Jason Monke59dc402018-08-16 12:05:01 -04005103 } else if (!belowChild) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01005104 // We are on a child
5105 return false;
5106 }
5107 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005108 }
Selim Cinek04fb2582015-06-02 19:58:09 +02005109 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005110 }
5111
Selim Cinekc22fff62016-05-20 12:44:30 -07005112 /** @hide */
5113 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005114 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07005115 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
5116 super.onInitializeAccessibilityEventInternal(event);
5117 event.setScrollable(mScrollable);
5118 event.setScrollX(mScrollX);
Selim Cinekc22fff62016-05-20 12:44:30 -07005119 event.setMaxScrollX(mScrollX);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005120 if (ANCHOR_SCROLLING) {
5121 // TODO
5122 } else {
5123 event.setScrollY(mOwnScrollY);
5124 event.setMaxScrollY(getScrollRange());
5125 }
Selim Cinekc22fff62016-05-20 12:44:30 -07005126 }
5127
5128 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005129 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07005130 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
5131 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07005132 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005133 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07005134 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005135 info.addAction(
5136 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
5137 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
5138 }
Selim Cinekef406062016-09-29 17:33:13 -07005139 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005140 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
5141 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
5142 }
5143 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07005144 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
5145 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07005146 }
5147
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005148 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb5605e52015-02-20 18:21:41 +01005149 public void generateChildOrderChangedEvent() {
5150 if (mIsExpanded && mAnimationsEnabled) {
5151 mGenerateChildOrderChangedEvent = true;
5152 mNeedsAnimation = true;
5153 requestChildrenUpdate();
5154 }
5155 }
5156
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005157 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005158 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005159 public int getContainerChildCount() {
5160 return getChildCount();
5161 }
5162
5163 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005164 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005165 public View getContainerChildAt(int i) {
5166 return getChildAt(i);
5167 }
5168
5169 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005170 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005171 public void removeContainerView(View v) {
5172 removeView(v);
5173 }
5174
5175 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005176 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005177 public void addContainerView(View v) {
5178 addView(v);
5179 }
5180
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005181 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek684a4422015-04-15 16:18:39 -07005182 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005183 mAnimationFinishedRunnables.add(runnable);
5184 }
5185
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005186 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
yoshiki iguchi4e30e762018-02-06 12:09:23 +09005187 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005188 mHeadsUpManager = headsUpManager;
Selim Cinek29aab962018-02-27 17:05:45 -08005189 mHeadsUpManager.addListener(mRoundnessManager);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04005190 mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005191 }
5192
Ned Burnsf81c4c42019-01-07 14:10:43 -05005193 public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
Evan Laird94492852018-10-25 13:43:01 -04005194 ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
5195 generateHeadsUpAnimation(row, isHeadsUp);
5196 }
5197
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005198 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005199 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07005200 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005201 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
5202 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07005203 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07005204 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07005205 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005206 requestChildrenUpdate();
5207 }
5208 }
5209
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005210 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005211 public void setShadeExpanded(boolean shadeExpanded) {
5212 mAmbientState.setShadeExpanded(shadeExpanded);
Selim Cineka59ecc32015-04-07 10:51:49 -07005213 mStateAnimator.setShadeExpanded(shadeExpanded);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005214 }
5215
Selim Cineka59ecc32015-04-07 10:51:49 -07005216 /**
5217 * Set the boundary for the bottom heads up position. The heads up will always be above this
5218 * position.
5219 *
Jason Monke59dc402018-08-16 12:05:01 -04005220 * @param height the height of the screen
Selim Cineka59ecc32015-04-07 10:51:49 -07005221 * @param bottomBarHeight the height of the bar on the bottom
5222 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005223 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka59ecc32015-04-07 10:51:49 -07005224 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
5225 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
5226 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005227 requestChildrenUpdate();
5228 }
5229
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005230 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005231 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
5232 mTrackingHeadsUp = row != null;
5233 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005234 }
5235
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005236 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaac93252015-04-14 20:04:12 -07005237 public void setScrimController(ScrimController scrimController) {
5238 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07005239 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07005240 }
5241
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005242 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005243 public void forceNoOverlappingRendering(boolean force) {
5244 mForceNoOverlappingRendering = force;
5245 }
5246
5247 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005248 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005249 public boolean hasOverlappingRendering() {
5250 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
5251 }
5252
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005253 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek6811d722016-01-19 17:53:12 -08005254 public void setAnimationRunning(boolean animationRunning) {
5255 if (animationRunning != mAnimationRunning) {
5256 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07005257 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005258 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07005259 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005260 }
5261 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08005262 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08005263 }
5264 }
5265
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005266 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3776fe02016-02-04 13:32:43 -08005267 public boolean isExpanded() {
5268 return mIsExpanded;
5269 }
5270
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005271 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin4e023812018-04-02 21:19:23 -07005272 public void setPulsing(boolean pulsing, boolean animated) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09005273 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01005274 return;
5275 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005276 mPulsing = pulsing;
Lucas Dupin4e023812018-04-02 21:19:23 -07005277 mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
Selim Cinekebf42342017-07-13 15:46:10 +02005278 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005279 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08005280 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08005281 updateContentHeight();
Adrian Roosd83e9992017-03-16 15:17:57 -07005282 requestChildrenUpdate();
Lucas Dupin4e023812018-04-02 21:19:23 -07005283 notifyHeightChangeListener(null, animated);
5284 mNeedsAnimation |= animated;
5285 }
5286
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005287 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin4e023812018-04-02 21:19:23 -07005288 private void generatePulsingAnimationEvent() {
5289 if (mNeedingPulseAnimation != null) {
5290 int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
5291 : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
5292 mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
5293 mNeedingPulseAnimation = null;
5294 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005295 }
5296
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005297 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07005298 public void setQsExpanded(boolean qsExpanded) {
5299 mQsExpanded = qsExpanded;
5300 updateAlgorithmLayoutMinHeight();
Riddle Hsu065c01c2018-05-10 23:14:19 +08005301 updateScrollability();
Selim Cinekbc243a92016-09-27 16:35:13 -07005302 }
5303
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005304 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005305 public void setQsExpansionFraction(float qsExpansionFraction) {
5306 mQsExpansionFraction = qsExpansionFraction;
5307 }
5308
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005309 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Gus Prevas0fa58d62019-01-11 13:58:40 -05005310 private void setOwnScrollY(int ownScrollY) {
5311 assert !ANCHOR_SCROLLING;
Selim Cinekef406062016-09-29 17:33:13 -07005312 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08005313 // We still want to call the normal scrolled changed for accessibility reasons
5314 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07005315 mOwnScrollY = ownScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -05005316 updateOnScrollChange();
5317 }
5318 }
5319
5320 private void updateOnScrollChange() {
5321 updateForwardAndBackwardScrollability();
5322 requestChildrenUpdate();
5323 }
5324
5325 private void updateScrollAnchor() {
5326 int anchorIndex = indexOfChild(mScrollAnchorView);
5327 // If the anchor view has been scrolled off the top, move to the next view.
5328 while (mScrollAnchorViewY < 0) {
5329 View nextAnchor = null;
5330 for (int i = anchorIndex + 1; i < getChildCount(); i++) {
5331 View child = getChildAt(i);
5332 if (child.getVisibility() != View.GONE
5333 && child instanceof ExpandableNotificationRow) {
5334 anchorIndex = i;
5335 nextAnchor = child;
5336 break;
5337 }
5338 }
5339 if (nextAnchor == null) {
5340 break;
5341 }
5342 mScrollAnchorViewY +=
5343 (int) (nextAnchor.getTranslationY() - mScrollAnchorView.getTranslationY());
5344 mScrollAnchorView = nextAnchor;
5345 }
5346 // If the view above the anchor view is fully visible, make it the anchor view.
5347 while (anchorIndex > 0 && mScrollAnchorViewY > 0) {
5348 View prevAnchor = null;
5349 for (int i = anchorIndex - 1; i >= 0; i--) {
5350 View child = getChildAt(i);
5351 if (child.getVisibility() != View.GONE
5352 && child instanceof ExpandableNotificationRow) {
5353 anchorIndex = i;
5354 prevAnchor = child;
5355 break;
5356 }
5357 }
5358 if (prevAnchor == null) {
5359 break;
5360 }
5361 float distanceToPreviousAnchor =
5362 mScrollAnchorView.getTranslationY() - prevAnchor.getTranslationY();
5363 if (distanceToPreviousAnchor < mScrollAnchorViewY) {
5364 mScrollAnchorViewY -= (int) distanceToPreviousAnchor;
5365 mScrollAnchorView = prevAnchor;
5366 }
Selim Cinekef406062016-09-29 17:33:13 -07005367 }
5368 }
5369
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005370 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005371 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07005372 int index = -1;
5373 if (mShelf != null) {
5374 index = indexOfChild(mShelf);
5375 removeView(mShelf);
5376 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08005377 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07005378 addView(mShelf, index);
5379 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07005380 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07005381 shelf.bind(mAmbientState, this);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005382 if (ANCHOR_SCROLLING) {
5383 mScrollAnchorView = mShelf;
5384 }
Selim Cinek281c2022016-10-13 19:14:43 -07005385 }
5386
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005387 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005388 public NotificationShelf getNotificationShelf() {
5389 return mShelf;
5390 }
5391
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005392 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekad7fac02016-10-18 17:09:15 -07005393 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
5394 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
5395 mMaxDisplayedNotifications = maxDisplayedNotifications;
5396 updateContentHeight();
5397 notifyHeightChangeListener(mShelf);
5398 }
5399 }
5400
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005401 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005402 public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
Jason Monke59dc402018-08-16 12:05:01 -04005403 mShouldShowShelfOnly = shouldShowShelfOnly;
shawnlin8e4e92c2018-04-12 18:47:24 +08005404 updateAlgorithmLayoutMinHeight();
5405 }
5406
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005407 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek48ff9b42016-11-09 19:31:51 -08005408 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07005409 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08005410 }
5411
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005412 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005413 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
5414 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
5415 updateClipping();
5416 }
5417
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005418 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005419 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
5420 mHeadsUpAnimatingAway = headsUpAnimatingAway;
5421 updateClipping();
5422 }
5423
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005424 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk297c04e2018-08-23 17:16:59 -04005425 @VisibleForTesting
5426 protected void setStatusBarState(int statusBarState) {
Selim Cinek355652a2016-12-07 13:32:12 -08005427 mStatusBarState = statusBarState;
5428 mAmbientState.setStatusBarState(statusBarState);
Evan Laird91d0f102018-09-18 17:39:55 -04005429 }
5430
5431 private void onStatePostChange() {
Jason Monk1fd3fc32018-08-14 17:20:09 -04005432 boolean onKeyguard = onKeyguard();
5433 boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
Evan Laird91d0f102018-09-18 17:39:55 -04005434
Jason Monk1fd3fc32018-08-14 17:20:09 -04005435 if (mHeadsUpAppearanceController != null) {
5436 mHeadsUpAppearanceController.setPublicMode(publicMode);
5437 }
5438
Beverly8fdb5332019-02-04 14:29:49 -05005439 SysuiStatusBarStateController state = (SysuiStatusBarStateController)
5440 Dependency.get(StatusBarStateController.class);
Jason Monk1fd3fc32018-08-14 17:20:09 -04005441 setHideSensitive(publicMode, state.goingToFullShade() /* animate */);
5442 setDimmed(onKeyguard, state.fromShadeLocked() /* animate */);
5443 setExpandingEnabled(!onKeyguard);
5444 ActivatableNotificationView activatedChild = getActivatedChild();
5445 setActivatedChild(null);
5446 if (activatedChild != null) {
5447 activatedChild.makeInactive(false /* animate */);
5448 }
Jason Monke59dc402018-08-16 12:05:01 -04005449 updateFooter();
Dave Mankoff57445802018-10-10 14:47:34 -04005450 requestChildrenUpdate();
Jason Monke59dc402018-08-16 12:05:01 -04005451 onUpdateRowStates();
Evan Laird91d0f102018-09-18 17:39:55 -04005452
5453 mEntryManager.updateNotifications();
Selim Cinek355652a2016-12-07 13:32:12 -08005454 }
5455
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005456 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd5ab6452016-12-08 16:34:00 -08005457 public void setExpandingVelocity(float expandingVelocity) {
5458 mAmbientState.setExpandingVelocity(expandingVelocity);
5459 }
5460
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005461 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005462 public float getOpeningHeight() {
5463 if (mEmptyShadeView.getVisibility() == GONE) {
5464 return getMinExpansionHeight();
5465 } else {
5466 return getAppearEndPosition();
5467 }
5468 }
5469
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005470 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005471 public void setIsFullWidth(boolean isFullWidth) {
5472 mAmbientState.setPanelFullWidth(isFullWidth);
5473 }
5474
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005475 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekec29d342017-05-05 18:31:49 -07005476 public void setUnlockHintRunning(boolean running) {
5477 mAmbientState.setUnlockHintRunning(running);
5478 }
5479
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005480 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005481 public void setQsCustomizerShowing(boolean isShowing) {
5482 mAmbientState.setQsCustomizerShowing(isShowing);
5483 requestChildrenUpdate();
5484 }
5485
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005486 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005487 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
5488 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
5489 }
5490
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005491 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin0cd882f2018-01-30 12:19:49 -08005492 public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
5493 mAntiBurnInOffsetX = antiBurnInOffsetX;
Bill Line60aa1e2018-06-13 18:07:15 +08005494 updatePanelTranslation();
Adrian Roosdc747bd2017-06-01 16:09:15 -07005495 }
5496
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005497 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek707e2072017-06-30 18:32:40 +02005498 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5499 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
shawnlin8e4e92c2018-04-12 18:47:24 +08005500 + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
5501 + " qsExpandFraction=%f]",
Selim Cinek707e2072017-06-30 18:32:40 +02005502 this.getClass().getSimpleName(),
Jason Monke59dc402018-08-16 12:05:01 -04005503 mPulsing ? "T" : "f",
5504 mAmbientState.isQsCustomizerShowing() ? "T" : "f",
Selim Cinek707e2072017-06-30 18:32:40 +02005505 getVisibility() == View.VISIBLE ? "visible"
5506 : getVisibility() == View.GONE ? "gone"
5507 : "invisible",
5508 getAlpha(),
shawnlin8e4e92c2018-04-12 18:47:24 +08005509 mAmbientState.getScrollY(),
5510 mMaxTopPadding,
Jason Monke59dc402018-08-16 12:05:01 -04005511 mShouldShowShelfOnly ? "T" : "f",
shawnlin8e4e92c2018-04-12 18:47:24 +08005512 mQsExpansionFraction));
Selim Cinek30887662018-10-15 17:37:21 -07005513 int childCount = getChildCount();
5514 pw.println(" Number of children: " + childCount);
5515 pw.println();
5516
5517 for (int i = 0; i < childCount; i++) {
5518 ExpandableView child = (ExpandableView) getChildAt(i);
5519 child.dump(fd, pw, args);
5520 if (!(child instanceof ExpandableNotificationRow)) {
5521 pw.println(" " + child.getClass().getSimpleName());
5522 // Notifications dump it's viewstate as part of their dump to support children
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005523 ExpandableViewState viewState = child.getViewState();
Selim Cinek30887662018-10-15 17:37:21 -07005524 if (viewState == null) {
5525 pw.println(" no viewState!!!");
5526 } else {
5527 pw.print(" ");
5528 viewState.dump(fd, pw, args);
5529 pw.println();
5530 pw.println();
5531 }
5532 }
5533 }
Selim Cinek30887662018-10-15 17:37:21 -07005534 int transientViewCount = getTransientViewCount();
Selim Cinekd4c32302018-11-19 19:43:14 -08005535 pw.println(" Transient Views: " + transientViewCount);
Selim Cinek30887662018-10-15 17:37:21 -07005536 for (int i = 0; i < transientViewCount; i++) {
5537 ExpandableView child = (ExpandableView) getTransientView(i);
5538 child.dump(fd, pw, args);
5539 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005540 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08005541 int draggedCount = draggedViews.size();
5542 pw.println(" Dragged Views: " + draggedCount);
5543 for (int i = 0; i < draggedCount; i++) {
5544 ExpandableView child = (ExpandableView) draggedViews.get(i);
5545 child.dump(fd, pw, args);
5546 }
Selim Cinek707e2072017-06-30 18:32:40 +02005547 }
5548
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005549 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin16cfe452018-02-08 13:14:50 -08005550 public boolean isFullyDark() {
5551 return mAmbientState.isFullyDark();
5552 }
5553
Selim Cinek7103fd42016-05-09 22:22:33 -04005554 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005555 * Add a listener whenever the expanded height changes. The first value passed as an argument
5556 * is the expanded height and the second one is the appearFraction.
5557 *
5558 * @param listener the listener to notify.
5559 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005560 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005561 public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
5562 mExpandedHeightListeners.add(listener);
5563 }
5564
5565 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07005566 * Stop a listener from listening to the expandedHeight.
5567 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005568 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek60ffea62018-03-22 13:16:44 -07005569 public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
5570 mExpandedHeightListeners.remove(listener);
5571 }
5572
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005573 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekf0c79e12018-05-14 17:17:31 -07005574 public void setHeadsUpAppearanceController(
5575 HeadsUpAppearanceController headsUpAppearanceController) {
5576 mHeadsUpAppearanceController = headsUpAppearanceController;
5577 }
5578
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005579 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek9bfc7a52018-06-11 16:09:00 -07005580 public void setIconAreaController(NotificationIconAreaController controller) {
5581 mIconAreaController = controller;
5582 }
5583
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005584 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005585 public void manageNotifications(View v) {
5586 Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
5587 mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
5588 }
5589
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005590 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005591 public void clearAllNotifications() {
5592 // animate-swipe all dismissable notifications, then animate the shade closed
5593 int numChildren = getChildCount();
5594
5595 final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
5596 final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
5597 for (int i = 0; i < numChildren; i++) {
5598 final View child = getChildAt(i);
5599 if (child instanceof ExpandableNotificationRow) {
5600 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
5601 boolean parentVisible = false;
5602 boolean hasClipBounds = child.getClipBounds(mTmpRect);
5603 if (canChildBeDismissed(child)) {
5604 viewsToRemove.add(row);
5605 if (child.getVisibility() == View.VISIBLE
5606 && (!hasClipBounds || mTmpRect.height() > 0)) {
5607 viewsToHide.add(child);
5608 parentVisible = true;
5609 }
5610 } else if (child.getVisibility() == View.VISIBLE
5611 && (!hasClipBounds || mTmpRect.height() > 0)) {
5612 parentVisible = true;
5613 }
5614 List<ExpandableNotificationRow> children = row.getNotificationChildren();
5615 if (children != null) {
5616 for (ExpandableNotificationRow childRow : children) {
5617 viewsToRemove.add(childRow);
5618 if (parentVisible && row.areChildrenExpanded()
5619 && canChildBeDismissed(childRow)) {
5620 hasClipBounds = childRow.getClipBounds(mTmpRect);
5621 if (childRow.getVisibility() == View.VISIBLE
5622 && (!hasClipBounds || mTmpRect.height() > 0)) {
5623 viewsToHide.add(childRow);
5624 }
5625 }
5626 }
5627 }
5628 }
5629 }
5630 if (viewsToRemove.isEmpty()) {
5631 mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
5632 return;
5633 }
5634
Jason Monk297c04e2018-08-23 17:16:59 -04005635 mShadeController.addPostCollapseAction(() -> {
Jason Monke59dc402018-08-16 12:05:01 -04005636 setDismissAllInProgress(false);
5637 for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
5638 if (canChildBeDismissed(rowToRemove)) {
5639 mEntryManager.removeNotification(rowToRemove.getEntry().key, null);
5640 } else {
5641 rowToRemove.resetTranslation();
5642 }
5643 }
5644 try {
5645 mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
5646 } catch (Exception ex) {
5647 }
5648 });
5649
5650 performDismissAllAnimations(viewsToHide);
5651 }
5652
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005653 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005654 public void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
5655 Runnable animationFinishAction = () -> {
5656 mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
5657 };
5658
5659 if (hideAnimatedList.isEmpty()) {
5660 animationFinishAction.run();
5661 return;
5662 }
5663
5664 // let's disable our normal animations
5665 setDismissAllInProgress(true);
5666
5667 // Decrease the delay for every row we animate to give the sense of
5668 // accelerating the swipes
5669 int rowDelayDecrement = 10;
5670 int currentDelay = 140;
5671 int totalDelay = 180;
5672 int numItems = hideAnimatedList.size();
5673 for (int i = numItems - 1; i >= 0; i--) {
5674 View view = hideAnimatedList.get(i);
5675 Runnable endRunnable = null;
5676 if (i == 0) {
5677 endRunnable = animationFinishAction;
5678 }
Lucas Dupinfb8bdbb2018-12-02 15:09:37 -08005679 dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
Jason Monke59dc402018-08-16 12:05:01 -04005680 currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
5681 totalDelay += currentDelay;
5682 }
5683 }
5684
5685 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005686 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005687 protected void inflateFooterView() {
5688 FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
5689 R.layout.status_bar_notification_footer, this, false);
5690 footerView.setDismissButtonClickListener(v -> {
5691 mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
5692 clearAllNotifications();
5693 });
5694 footerView.setManageButtonClickListener(this::manageNotifications);
5695 setFooterView(footerView);
5696 }
5697
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005698 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5699 private void inflateEmptyShadeView() {
Jason Monke59dc402018-08-16 12:05:01 -04005700 EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
5701 R.layout.status_bar_no_notifications, this, false);
5702 view.setText(R.string.empty_shade_text);
5703 setEmptyShadeView(view);
5704 }
5705
5706 /**
5707 * Updates expanded, dimmed and locked states of notification rows.
5708 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005709 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jason Monke59dc402018-08-16 12:05:01 -04005710 public void onUpdateRowStates() {
5711 changeViewPosition(mFooterView, -1);
5712
5713 // The following views will be moved to the end of mStackScroller. This counter represents
5714 // the offset from the last child. Initialized to 1 for the very last position. It is post-
5715 // incremented in the following "changeViewPosition" calls so that its value is correct for
5716 // subsequent calls.
5717 int offsetFromEnd = 1;
5718 changeViewPosition(mEmptyShadeView,
5719 getChildCount() - offsetFromEnd++);
5720
5721 // No post-increment for this call because it is the last one. Make sure to add one if
5722 // another "changeViewPosition" call is ever added.
5723 changeViewPosition(mShelf,
5724 getChildCount() - offsetFromEnd);
Jason Monke59dc402018-08-16 12:05:01 -04005725 }
5726
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005727 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5728 public void setNotificationPanel(NotificationPanelView notificationPanelView) {
Jason Monke59dc402018-08-16 12:05:01 -04005729 mNotificationPanel = notificationPanelView;
5730 }
5731
Jason Monk297c04e2018-08-23 17:16:59 -04005732 public void updateIconAreaViews() {
5733 mIconAreaController.updateNotificationIcons();
5734 }
5735
Selim Cinek60ffea62018-03-22 13:16:44 -07005736 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005737 * A listener that is notified when the empty space below the notifications is clicked on
5738 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005739 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005740 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08005741 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005742 }
5743
5744 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02005745 * A listener that gets notified when the overscroll at the top has changed.
5746 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005747 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi290600a2014-05-30 17:02:20 +02005748 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02005749
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005750 /**
5751 * Notifies a listener that the overscroll has changed.
5752 *
5753 * @param amount the amount of overscroll, in pixels
5754 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
5755 * unrubberbanded motion to directly expand overscroll view (e.g
5756 * expand
5757 * QS)
5758 */
5759 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02005760
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005761 /**
5762 * Notify a listener that the scroller wants to escape from the scrolling motion and
5763 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
5764 *
5765 * @param velocity The velocity that the Scroller had when over flinging
5766 * @param open Should the fling open or close the overscroll view.
5767 */
5768 void flingTopOverscroll(float velocity, boolean open);
5769 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02005770
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005771 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5772 public boolean hasActiveNotifications() {
Jason Monke59dc402018-08-16 12:05:01 -04005773 return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
5774 }
5775
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04005776 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005777 public void updateSpeedBumpIndex() {
Jason Monke59dc402018-08-16 12:05:01 -04005778 int speedBumpIndex = 0;
5779 int currentIndex = 0;
5780 final int N = getChildCount();
5781 for (int i = 0; i < N; i++) {
5782 View view = getChildAt(i);
5783 if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
5784 continue;
5785 }
5786 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
5787 currentIndex++;
Gus Prevas33619af2018-10-26 15:40:27 -04005788 boolean beforeSpeedBump;
5789 if (mLowPriorityBeforeSpeedBump) {
Gus Prevascaed15c2019-01-18 14:19:51 -05005790 beforeSpeedBump = !row.getEntry().ambient;
Gus Prevas33619af2018-10-26 15:40:27 -04005791 } else {
Gus Prevascaed15c2019-01-18 14:19:51 -05005792 beforeSpeedBump = row.getEntry().isHighPriority();
Gus Prevas33619af2018-10-26 15:40:27 -04005793 }
5794 if (beforeSpeedBump) {
Jason Monke59dc402018-08-16 12:05:01 -04005795 speedBumpIndex = currentIndex;
5796 }
5797 }
5798 boolean noAmbient = speedBumpIndex == N;
5799 updateSpeedBumpIndex(speedBumpIndex, noAmbient);
5800 }
5801
Gus Prevase2d6f042018-10-17 15:25:30 -04005802 /** Updates the indices of the boundaries between sections. */
5803 @ShadeViewRefactor(RefactorComponent.INPUT)
5804 public void updateSectionBoundaries() {
5805 int gapIndex = -1;
5806 if (NotificationUtils.useNewInterruptionModel(mContext)) {
5807 int currentIndex = 0;
5808 final int n = getChildCount();
5809 for (int i = 0; i < n; i++) {
5810 View view = getChildAt(i);
5811 if (view.getVisibility() == View.GONE
5812 || !(view instanceof ExpandableNotificationRow)) {
5813 continue;
5814 }
5815 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Gus Prevascaed15c2019-01-18 14:19:51 -05005816 if (!row.getEntry().isHighPriority()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04005817 if (currentIndex > 0) {
5818 gapIndex = currentIndex;
5819 }
5820 break;
5821 }
5822 currentIndex++;
5823 }
5824 }
5825 mAmbientState.setSectionBoundaryIndex(0, gapIndex);
5826 }
5827
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005828 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08005829 private void updateContinuousShadowDrawing() {
5830 boolean continuousShadowUpdate = mAnimationRunning
5831 || !mAmbientState.getDraggedViews().isEmpty();
5832 if (continuousShadowUpdate != mContinuousShadowUpdate) {
5833 if (continuousShadowUpdate) {
5834 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
5835 } else {
5836 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
5837 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07005838 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08005839 }
5840 }
5841
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005842 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005843 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -08005844 public void resetExposedMenuView(boolean animate, boolean force) {
5845 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07005846 }
5847
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005848 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
5849 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02005850
Jason Monke59dc402018-08-16 12:05:01 -04005851 static AnimationFilter[] FILTERS = new AnimationFilter[]{
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005852
5853 // ANIMATION_TYPE_ADD
5854 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005855 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005856 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005857 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005858 .animateZ()
5859 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005860
5861 // ANIMATION_TYPE_REMOVE
5862 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005863 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005864 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005865 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005866 .animateZ()
5867 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005868
5869 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5870 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005871 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005872 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005873 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005874 .animateZ()
5875 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005876
5877 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5878 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005879 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005880 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005881 .animateY()
5882 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005883 .animateZ(),
5884
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005885 // ANIMATION_TYPE_ACTIVATED_CHILD
5886 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005887 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005888
5889 // ANIMATION_TYPE_DIMMED
5890 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005891 .animateDimmed(),
5892
5893 // ANIMATION_TYPE_CHANGE_POSITION
5894 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005895 .animateAlpha() // maybe the children change positions
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005896 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005897 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005898 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04005899 .animateZ(),
5900
5901 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07005902 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005903
5904 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5905 new AnimationFilter()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005906 .animateHeight()
5907 .animateTopInset()
5908 .animateY()
5909 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02005910 .animateZ()
5911 .hasDelays(),
5912
5913 // ANIMATION_TYPE_HIDE_SENSITIVE
5914 new AnimationFilter()
5915 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02005916
5917 // ANIMATION_TYPE_VIEW_RESIZE
5918 new AnimationFilter()
Selim Cineka5e211b2014-08-11 17:35:48 +02005919 .animateHeight()
5920 .animateTopInset()
5921 .animateY()
5922 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02005923
Selim Cinekb5605e52015-02-20 18:21:41 +01005924 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
5925 new AnimationFilter()
5926 .animateAlpha()
5927 .animateHeight()
5928 .animateTopInset()
5929 .animateY()
5930 .animateZ(),
5931
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005932 // ANIMATION_TYPE_HEADS_UP_APPEAR
5933 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005934 .animateHeight()
5935 .animateTopInset()
5936 .animateY()
5937 .animateZ(),
5938
5939 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5940 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005941 .animateHeight()
5942 .animateTopInset()
5943 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07005944 .animateZ()
5945 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005946
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005947 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5948 new AnimationFilter()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005949 .animateHeight()
5950 .animateTopInset()
5951 .animateY()
5952 .animateZ()
5953 .hasDelays(),
5954
Selim Cineka59ecc32015-04-07 10:51:49 -07005955 // ANIMATION_TYPE_HEADS_UP_OTHER
5956 new AnimationFilter()
Selim Cineka59ecc32015-04-07 10:51:49 -07005957 .animateHeight()
5958 .animateTopInset()
5959 .animateY()
5960 .animateZ(),
5961
Selim Cinekd9acca52014-09-01 22:33:25 +02005962 // ANIMATION_TYPE_EVERYTHING
5963 new AnimationFilter()
5964 .animateAlpha()
5965 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02005966 .animateDimmed()
5967 .animateHideSensitive()
5968 .animateHeight()
5969 .animateTopInset()
5970 .animateY()
5971 .animateZ(),
Lucas Dupin4e023812018-04-02 21:19:23 -07005972
5973 // ANIMATION_TYPE_PULSE_APPEAR
5974 new AnimationFilter()
5975 .animateAlpha()
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005976 .hasDelays()
Lucas Dupin4e023812018-04-02 21:19:23 -07005977 .animateY(),
5978
5979 // ANIMATION_TYPE_PULSE_DISAPPEAR
5980 new AnimationFilter()
5981 .animateAlpha()
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07005982 .hasDelays()
Lucas Dupin4e023812018-04-02 21:19:23 -07005983 .animateY(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005984 };
5985
Jason Monke59dc402018-08-16 12:05:01 -04005986 static int[] LENGTHS = new int[]{
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005987
5988 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005989 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005990
5991 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005992 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005993
5994 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5995 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5996
5997 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5998 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5999
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006000 // ANIMATION_TYPE_ACTIVATED_CHILD
6001 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
6002
6003 // ANIMATION_TYPE_DIMMED
6004 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006005
6006 // ANIMATION_TYPE_CHANGE_POSITION
6007 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04006008
6009 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07006010 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006011
6012 // ANIMATION_TYPE_GO_TO_FULL_SHADE
6013 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02006014
6015 // ANIMATION_TYPE_HIDE_SENSITIVE
6016 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02006017
6018 // ANIMATION_TYPE_VIEW_RESIZE
6019 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02006020
Selim Cinekb5605e52015-02-20 18:21:41 +01006021 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08006022 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01006023
Selim Cinekb8f09cf2015-03-16 17:09:28 -07006024 // ANIMATION_TYPE_HEADS_UP_APPEAR
6025 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
6026
6027 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
6028 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
6029
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07006030 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
6031 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
6032
Selim Cineka59ecc32015-04-07 10:51:49 -07006033 // ANIMATION_TYPE_HEADS_UP_OTHER
6034 StackStateAnimator.ANIMATION_DURATION_STANDARD,
6035
Selim Cinekd9acca52014-09-01 22:33:25 +02006036 // ANIMATION_TYPE_EVERYTHING
6037 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Lucas Dupin4e023812018-04-02 21:19:23 -07006038
6039 // ANIMATION_TYPE_PULSE_APPEAR
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07006040 StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR,
Lucas Dupin4e023812018-04-02 21:19:23 -07006041
6042 // ANIMATION_TYPE_PULSE_DISAPPEAR
Lucas Dupin2a6bdfd2018-06-12 17:45:21 -07006043 StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR / 2,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006044 };
6045
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006046 static final int ANIMATION_TYPE_ADD = 0;
6047 static final int ANIMATION_TYPE_REMOVE = 1;
6048 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
6049 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
Selim Cinekff2ffec2018-11-19 18:52:01 -08006050 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 4;
6051 static final int ANIMATION_TYPE_DIMMED = 5;
6052 static final int ANIMATION_TYPE_CHANGE_POSITION = 6;
6053 static final int ANIMATION_TYPE_DARK = 7;
6054 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 8;
6055 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 9;
6056 static final int ANIMATION_TYPE_VIEW_RESIZE = 10;
6057 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 11;
6058 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 12;
6059 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 13;
6060 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 14;
6061 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 15;
6062 static final int ANIMATION_TYPE_EVERYTHING = 16;
6063 static final int ANIMATION_TYPE_PULSE_APPEAR = 17;
6064 static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 18;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02006065
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01006066 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
6067 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
6068
Selim Cinek572bbd42014-04-25 16:43:27 +02006069 final long eventStartTime;
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006070 final ExpandableView mChangingView;
Selim Cinek572bbd42014-04-25 16:43:27 +02006071 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02006072 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006073 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006074 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01006075 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07006076 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02006077
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006078 AnimationEvent(ExpandableView view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006079 this(view, type, LENGTHS[type]);
6080 }
6081
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006082 AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006083 this(view, type, LENGTHS[type], filter);
6084 }
6085
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006086 AnimationEvent(ExpandableView view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006087 this(view, type, length, FILTERS[type]);
6088 }
6089
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006090 AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02006091 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006092 mChangingView = view;
Selim Cinek572bbd42014-04-25 16:43:27 +02006093 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006094 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07006095 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006096 }
6097
6098 /**
6099 * Combines the length of several animation events into a single value.
6100 *
6101 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006102 * @return The combined length. Depending on the event types, this might be the maximum of
Jason Monke59dc402018-08-16 12:05:01 -04006103 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006104 */
6105 static long combineLength(ArrayList<AnimationEvent> events) {
6106 long length = 0;
6107 int size = events.size();
6108 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006109 AnimationEvent event = events.get(i);
6110 length = Math.max(length, event.length);
6111 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
6112 return event.length;
6113 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006114 }
6115 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02006116 }
6117 }
Jason Monke59dc402018-08-16 12:05:01 -04006118
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006119 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
6120 private final StateListener mStateListener = new StateListener() {
Jason Monke59dc402018-08-16 12:05:01 -04006121 @Override
6122 public void onStatePreChange(int oldState, int newState) {
6123 if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
6124 requestAnimateEverything();
6125 }
6126 }
6127
6128 @Override
6129 public void onStateChanged(int newState) {
6130 setStatusBarState(newState);
6131 }
Evan Laird91d0f102018-09-18 17:39:55 -04006132
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006133 @Override
6134 public void onStatePostChange() {
Evan Laird91d0f102018-09-18 17:39:55 -04006135 NotificationStackScrollLayout.this.onStatePostChange();
6136 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006137 };
6138
Will Brockmane718d582019-01-17 16:38:38 -05006139 @VisibleForTesting
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006140 @ShadeViewRefactor(RefactorComponent.INPUT)
Will Brockmane718d582019-01-17 16:38:38 -05006141 protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006142 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006143 public void onMenuClicked(View view, int x, int y, MenuItem item) {
6144 if (mLongPressListener == null) {
6145 return;
6146 }
6147 if (view instanceof ExpandableNotificationRow) {
6148 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Will Brockmane718d582019-01-17 16:38:38 -05006149 mMetricsLogger.write(row.getStatusBarNotification().getLogMaker()
6150 .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
6151 .setType(MetricsEvent.TYPE_ACTION)
6152 );
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006153 }
6154 mLongPressListener.onLongPress(view, x, y, item);
6155 }
6156
6157 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006158 public void onMenuReset(View row) {
6159 View translatingParentView = mSwipeHelper.getTranslatingParentView();
6160 if (translatingParentView != null && row == translatingParentView) {
6161 mSwipeHelper.clearExposedMenuView();
6162 mSwipeHelper.clearTranslatingParentView();
Gus Prevas211181532018-12-13 14:49:33 -05006163 if (row instanceof ExpandableNotificationRow) {
6164 mHeadsUpManager.setMenuShown(
6165 ((ExpandableNotificationRow) row).getEntry(), false);
6166
6167 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006168 }
6169 }
6170
6171 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006172 public void onMenuShown(View row) {
6173 if (row instanceof ExpandableNotificationRow) {
Gus Prevas211181532018-12-13 14:49:33 -05006174 ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
Will Brockmane718d582019-01-17 16:38:38 -05006175 mMetricsLogger.write(notificationRow.getStatusBarNotification().getLogMaker()
6176 .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
6177 .setType(MetricsEvent.TYPE_ACTION));
Gus Prevas211181532018-12-13 14:49:33 -05006178 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006179 }
6180 mSwipeHelper.onMenuShown(row);
6181 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006182 };
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006183
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006184 @ShadeViewRefactor(RefactorComponent.INPUT)
6185 private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
6186 new NotificationSwipeHelper.NotificationCallback() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006187 @Override
6188 public void onDismiss() {
6189 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
6190 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
6191 false /* resetMenu */);
6192 }
6193
6194 @Override
6195 public void onSnooze(StatusBarNotification sbn,
6196 NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
6197 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
6198 }
6199
6200 @Override
6201 public boolean isExpanded() {
6202 return NotificationStackScrollLayout.this.isExpanded();
6203 }
6204
6205 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006206 public void onDragCancelled(View v) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006207 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006208 mFalsingManager.onNotificatonStopDismissing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006209 }
6210
6211 /**
6212 * Handles cleanup after the given {@code view} has been fully swiped out (including
6213 * re-invoking dismiss logic in case the notification has not made its way out yet).
6214 */
6215 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006216 public void onChildDismissed(View view) {
6217 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6218 if (!row.isDismissed()) {
6219 handleChildViewDismissed(view);
6220 }
6221 ViewGroup transientContainer = row.getTransientContainer();
6222 if (transientContainer != null) {
6223 transientContainer.removeTransientView(view);
6224 }
6225 }
6226
6227 /**
6228 * Starts up notification dismiss and tells the notification, if any, to remove itself from
6229 * layout.
6230 *
6231 * @param view view (e.g. notification) to dismiss from the layout
6232 */
6233
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006234 public void handleChildViewDismissed(View view) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006235 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006236 if (mDismissAllInProgress) {
6237 return;
6238 }
6239
6240 boolean isBlockingHelperShown = false;
6241
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006242 mAmbientState.onDragFinished(view);
6243 updateContinuousShadowDrawing();
6244
6245 if (view instanceof ExpandableNotificationRow) {
6246 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6247 if (row.isHeadsUp()) {
6248 mHeadsUpManager.addSwipedOutNotification(
6249 row.getStatusBarNotification().getKey());
6250 }
6251 isBlockingHelperShown =
6252 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
6253 }
6254
6255 if (!isBlockingHelperShown) {
6256 mSwipedOutViews.add(view);
6257 }
6258 mFalsingManager.onNotificationDismissed();
6259 if (mFalsingManager.shouldEnforceBouncer()) {
6260 mStatusBar.executeRunnableDismissingKeyguard(
6261 null,
6262 null /* cancelAction */,
6263 false /* dismissShade */,
6264 true /* afterKeyguardGone */,
6265 false /* deferred */);
6266 }
6267 }
6268
6269 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006270 public boolean isAntiFalsingNeeded() {
6271 return onKeyguard();
6272 }
6273
6274 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006275 public View getChildAtPosition(MotionEvent ev) {
6276 View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
6277 ev.getY());
6278 if (child instanceof ExpandableNotificationRow) {
6279 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
6280 ExpandableNotificationRow parent = row.getNotificationParent();
6281 if (parent != null && parent.areChildrenExpanded()
6282 && (parent.areGutsExposed()
6283 || mSwipeHelper.getExposedMenuView() == parent
6284 || (parent.getNotificationChildren().size() == 1
Evan Laird94492852018-10-25 13:43:01 -04006285 && parent.getEntry().isClearable()))) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006286 // In this case the group is expanded and showing the menu for the
6287 // group, further interaction should apply to the group, not any
6288 // child notifications so we use the parent of the child. We also do the same
6289 // if we only have a single child.
6290 child = parent;
6291 }
6292 }
6293 return child;
6294 }
6295
6296 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006297 public void onBeginDrag(View v) {
6298 mFalsingManager.onNotificatonStartDismissing();
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006299 setSwipingInProgress(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006300 mAmbientState.onBeginDrag((ExpandableView) v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006301 updateContinuousShadowDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006302 requestChildrenUpdate();
6303 }
6304
6305 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006306 public void onChildSnappedBack(View animView, float targetLeft) {
6307 mAmbientState.onDragFinished(animView);
6308 updateContinuousShadowDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006309 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
6310 if (menuRow != null && targetLeft == 0) {
6311 menuRow.resetMenu();
6312 mSwipeHelper.clearCurrentMenuRow();
6313 }
6314 }
6315
6316 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006317 public boolean updateSwipeProgress(View animView, boolean dismissable,
6318 float swipeProgress) {
6319 // Returning true prevents alpha fading.
6320 return !mFadeNotificationsOnDismiss;
6321 }
6322
6323 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006324 public float getFalsingThresholdFactor() {
6325 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
6326 }
6327
6328 @Override
Gus Prevasc4e68d42019-01-17 15:45:21 -05006329 public int getConstrainSwipeStartPosition() {
6330 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
6331 if (menuRow != null) {
6332 return Math.abs(menuRow.getMenuSnapTarget());
6333 }
6334 return 0;
6335 }
6336
6337 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006338 public boolean canChildBeDismissed(View v) {
6339 return NotificationStackScrollLayout.this.canChildBeDismissed(v);
6340 }
Gus Prevas37d67e22018-11-02 14:48:55 -04006341
6342 @Override
6343 public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
Gus Prevas50e38992018-11-14 10:33:44 -05006344 boolean isValidDirection;
6345 if (NotificationUtils.useNewInterruptionModel(mContext)) {
Gus Prevasa18dc572019-01-14 16:11:22 -05006346 isValidDirection = mDismissRtl ? !isRightOrDown : isRightOrDown;
Gus Prevas50e38992018-11-14 10:33:44 -05006347 } else {
6348 isValidDirection = true;
6349 }
6350 return isValidDirection && canChildBeDismissed(v);
Gus Prevas37d67e22018-11-02 14:48:55 -04006351 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006352 };
6353
6354 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
6355
6356 @ShadeViewRefactor(RefactorComponent.INPUT)
6357 private final DragDownCallback mDragDownCallback = new DragDownCallback() {
6358
6359 /* Only ever called as a consequence of a lockscreen expansion gesture. */
6360 @Override
6361 public boolean onDraggedDown(View startingChild, int dragLengthY) {
6362 if (mStatusBarState == StatusBarState.KEYGUARD
Lucas Dupin55c6e802018-09-27 18:07:36 -07006363 && hasActiveNotifications()) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006364 mLockscreenGestureLogger.write(
6365 MetricsEvent.ACTION_LS_SHADE,
6366 (int) (dragLengthY / mDisplayMetrics.density),
6367 0 /* velocityDp - N/A */);
6368
Lucas Dupinc7804042018-12-21 12:26:33 -08006369 if (!mAmbientState.isDark() || startingChild != null) {
Lucas Dupin55c6e802018-09-27 18:07:36 -07006370 // We have notifications, go to locked shade.
Jason Monk297c04e2018-08-23 17:16:59 -04006371 mShadeController.goToLockedShade(startingChild);
Lucas Dupin55c6e802018-09-27 18:07:36 -07006372 if (startingChild instanceof ExpandableNotificationRow) {
6373 ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
6374 row.onExpandedByGesture(true /* drag down is always an open */);
6375 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006376 }
Lucas Dupin55c6e802018-09-27 18:07:36 -07006377
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006378 return true;
6379 } else {
6380 // abort gesture.
6381 return false;
6382 }
6383 }
6384
6385 @Override
6386 public void onDragDownReset() {
6387 setDimmed(true /* dimmed */, true /* animated */);
6388 resetScrollPosition();
6389 resetCheckSnoozeLeavebehind();
6390 }
6391
6392 @Override
6393 public void onCrossedThreshold(boolean above) {
6394 setDimmed(!above /* dimmed */, true /* animate */);
6395 }
6396
6397 @Override
6398 public void onTouchSlopExceeded() {
6399 cancelLongPress();
6400 checkSnoozeLeavebehind();
6401 }
6402
6403 @Override
6404 public void setEmptyDragAmount(float amount) {
6405 mNotificationPanel.setEmptyDragAmount(amount);
6406 }
6407
6408 @Override
6409 public boolean isFalsingCheckNeeded() {
6410 return mStatusBarState == StatusBarState.KEYGUARD;
6411 }
6412 };
6413
6414 public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
6415
6416 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6417 private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
6418 @Override
6419 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6420 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6421 }
6422
6423 @Override
6424 public boolean isExpanded() {
6425 return mIsExpanded;
6426 }
6427
6428 @Override
6429 public Context getContext() {
6430 return mContext;
6431 }
6432 };
6433
6434 public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
6435
6436
6437 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6438 private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
6439 @Override
6440 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
6441 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
6442 && (mIsExpanded || changedRow.isPinned());
6443 if (animated) {
6444 mExpandedGroupView = changedRow;
6445 mNeedsAnimation = true;
6446 }
6447 changedRow.setChildrenExpanded(expanded, animated);
6448 if (!mGroupExpandedForMeasure) {
6449 onHeightChanged(changedRow, false /* needsAnimation */);
6450 }
6451 runAfterAnimationFinished(new Runnable() {
6452 @Override
6453 public void run() {
6454 changedRow.onFinishedExpansionChange();
6455 }
6456 });
6457 }
6458
6459 @Override
6460 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
6461 mStatusBar.requestNotificationUpdate();
6462 }
6463
6464 @Override
6465 public void onGroupsChanged() {
6466 mStatusBar.requestNotificationUpdate();
6467 }
6468 };
6469
6470 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6471 private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
6472 @Override
6473 public ExpandableView getChildAtPosition(float touchX, float touchY) {
6474 return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY);
6475 }
6476
6477 @Override
6478 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6479 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6480 }
6481
6482 @Override
6483 public boolean canChildBeExpanded(View v) {
6484 return v instanceof ExpandableNotificationRow
6485 && ((ExpandableNotificationRow) v).isExpandable()
6486 && !((ExpandableNotificationRow) v).areGutsExposed()
6487 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
6488 }
6489
6490 /* Only ever called as a consequence of an expansion gesture in the shade. */
6491 @Override
6492 public void setUserExpandedChild(View v, boolean userExpanded) {
6493 if (v instanceof ExpandableNotificationRow) {
6494 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
6495 if (userExpanded && onKeyguard()) {
6496 // Due to a race when locking the screen while touching, a notification may be
6497 // expanded even after we went back to keyguard. An example of this happens if
6498 // you click in the empty space while expanding a group.
6499
6500 // We also need to un-user lock it here, since otherwise the content height
6501 // calculated might be wrong. We also can't invert the two calls since
6502 // un-userlocking it will trigger a layout switch in the content view.
6503 row.setUserLocked(false);
6504 updateContentHeight();
6505 notifyHeightChangeListener(row);
6506 return;
6507 }
6508 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
6509 row.onExpandedByGesture(userExpanded);
6510 }
6511 }
6512
6513 @Override
6514 public void setExpansionCancelled(View v) {
6515 if (v instanceof ExpandableNotificationRow) {
6516 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
6517 }
6518 }
6519
6520 @Override
6521 public void setUserLockedChild(View v, boolean userLocked) {
6522 if (v instanceof ExpandableNotificationRow) {
6523 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
6524 }
6525 cancelLongPress();
6526 requestDisallowInterceptTouchEvent(true);
6527 }
6528
6529 @Override
6530 public void expansionStateChanged(boolean isExpanding) {
6531 mExpandingNotification = isExpanding;
6532 if (!mExpandedInThisMotion) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05006533 if (ANCHOR_SCROLLING) {
6534 // TODO
6535 } else {
6536 mMaxScrollAfterExpand = mOwnScrollY;
6537 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006538 mExpandedInThisMotion = true;
6539 }
6540 }
6541
6542 @Override
6543 public int getMaxExpandHeight(ExpandableView view) {
6544 return view.getMaxContentHeight();
6545 }
6546 };
6547
6548 public ExpandHelper.Callback getExpandHelperCallback() {
6549 return mExpandHelperCallback;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006550 }
Selim Cinek67b22602014-03-10 15:40:16 +01006551}