blob: 84a293eebc95ae2506a9a2020fcb16cebbe19a74 [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;
Evan Laird25f02752019-08-14 19:25:06 -040021import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
Gus Prevas0fa58d62019-01-11 13:58:40 -050022import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
Gus Prevase83700cb2018-12-14 11:42:51 -050023import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
Selim Cinekda330ff2019-06-11 15:41:28 -070024import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
Gus Prevas59ec2ff2018-12-28 16:20:28 -050025import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
Selim Cinek2627d722018-01-19 12:16:49 -080026
Ned Burns61269442019-05-02 18:27:23 -040027import static java.lang.annotation.RetentionPolicy.SOURCE;
28
Selim Cinek614576e2016-01-20 10:54:09 -080029import android.animation.Animator;
30import android.animation.AnimatorListenerAdapter;
Selim Cinekd35c2792016-01-21 13:20:57 -080031import android.animation.TimeAnimator;
32import android.animation.ValueAnimator;
Ned Burns61269442019-05-02 18:27:23 -040033import android.annotation.IntDef;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040034import android.annotation.NonNull;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010035import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010036import android.content.Context;
Jason Monke59dc402018-08-16 12:05:01 -040037import android.content.Intent;
Lucas Dupin28790c02020-01-30 14:18:41 -080038import android.content.pm.UserInfo;
Selim Cinek67b22602014-03-10 15:40:16 +010039import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080040import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010041import android.graphics.Canvas;
Lucas Dupind285cf02018-01-18 09:18:23 -080042import android.graphics.Color;
Lucas Dupin64e2f572019-03-21 14:21:14 -070043import android.graphics.Outline;
Selim Cinek67b22602014-03-10 15:40:16 +010044import android.graphics.Paint;
Evan Lairde55c6012019-03-13 12:54:37 -040045import android.graphics.Point;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010046import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080047import android.graphics.PorterDuff;
48import android.graphics.PorterDuffXfermode;
49import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070050import android.os.Bundle;
Jason Monke59dc402018-08-16 12:05:01 -040051import android.os.ServiceManager;
52import android.provider.Settings;
Mady Mellorc2ff0112019-03-28 14:18:06 -070053import android.service.notification.NotificationListenerService;
Mady Mellor95d743c2017-01-10 12:05:27 -080054import android.service.notification.StatusBarNotification;
Selim Cinek67b22602014-03-10 15:40:16 +010055import android.util.AttributeSet;
Jason Monke59dc402018-08-16 12:05:01 -040056import android.util.DisplayMetrics;
Selim Cinek67b22602014-03-10 15:40:16 +010057import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080058import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070059import android.util.Pair;
Lucas Dupin28790c02020-01-30 14:18:41 -080060import android.util.SparseArray;
Lucas Dupine17ce522017-07-17 15:45:06 -070061import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070062import android.view.InputDevice;
Jason Monke59dc402018-08-16 12:05:01 -040063import android.view.LayoutInflater;
Selim Cinek67b22602014-03-10 15:40:16 +010064import android.view.MotionEvent;
65import android.view.VelocityTracker;
66import android.view.View;
67import android.view.ViewConfiguration;
68import android.view.ViewGroup;
Lucas Dupin64e2f572019-03-21 14:21:14 -070069import android.view.ViewOutlineProvider;
Selim Cinek343e6e22014-04-11 21:23:30 +020070import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070071import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070072import android.view.accessibility.AccessibilityEvent;
73import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020074import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080075import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010076import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070077import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070078
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040079import com.android.internal.annotations.VisibleForTesting;
80import com.android.internal.graphics.ColorUtils;
Mady Mellora41587b2016-02-11 18:43:06 -080081import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010082import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monke59dc402018-08-16 12:05:01 -040083import com.android.internal.statusbar.IStatusBarService;
Lucas Dupin4e023812018-04-02 21:19:23 -070084import com.android.keyguard.KeyguardSliceView;
Lucas Dupine17ce522017-07-17 15:45:06 -070085import com.android.settingslib.Utils;
Rohan Shah524cf7b2018-03-15 14:40:02 -070086import com.android.systemui.Dependency;
Jason Monke59dc402018-08-16 12:05:01 -040087import com.android.systemui.Dumpable;
Selim Cinek67b22602014-03-10 15:40:16 +010088import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080089import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010090import com.android.systemui.R;
91import com.android.systemui.SwipeHelper;
Jason Monke59dc402018-08-16 12:05:01 -040092import com.android.systemui.colorextraction.SysuiColorExtractor;
Dave Mankoff468d4f62019-05-08 14:56:29 -040093import com.android.systemui.plugins.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080094import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
95import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040096import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
Mady Mellor95d743c2017-01-10 12:05:27 -080097import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Beverly8fdb5332019-02-04 14:29:49 -050098import com.android.systemui.plugins.statusbar.StatusBarStateController;
99import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Jason Monke59dc402018-08-16 12:05:01 -0400100import com.android.systemui.statusbar.CommandQueue;
101import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
Jorim Jaggia2052ea2014-08-05 16:22:30 +0200102import com.android.systemui.statusbar.EmptyShadeView;
Jason Monk297c04e2018-08-23 17:16:59 -0400103import com.android.systemui.statusbar.NotificationLockscreenUserManager;
Lucas Dupin28790c02020-01-30 14:18:41 -0800104import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
Jason Monk297c04e2018-08-23 17:16:59 -0400105import com.android.systemui.statusbar.NotificationRemoteInputManager;
106import com.android.systemui.statusbar.NotificationShelf;
107import com.android.systemui.statusbar.RemoteInputController;
108import com.android.systemui.statusbar.StatusBarState;
Beverly8fdb5332019-02-04 14:29:49 -0500109import com.android.systemui.statusbar.SysuiStatusBarStateController;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700110import com.android.systemui.statusbar.notification.DynamicPrivacyController;
Jason Monk297c04e2018-08-23 17:16:59 -0400111import com.android.systemui.statusbar.notification.FakeShadowView;
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500112import com.android.systemui.statusbar.notification.NotificationEntryListener;
Jason Monk297c04e2018-08-23 17:16:59 -0400113import com.android.systemui.statusbar.notification.NotificationEntryManager;
114import com.android.systemui.statusbar.notification.NotificationUtils;
115import com.android.systemui.statusbar.notification.ShadeViewRefactor;
116import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
Jason Monk297c04e2018-08-23 17:16:59 -0400117import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -0500118import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Jason Monk297c04e2018-08-23 17:16:59 -0400119import com.android.systemui.statusbar.notification.logging.NotificationLogger;
120import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
Rohan Shah20790b82018-07-02 17:21:04 -0700121import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
122import com.android.systemui.statusbar.notification.row.ExpandableView;
123import com.android.systemui.statusbar.notification.row.FooterView;
124import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
Rohan Shah20790b82018-07-02 17:21:04 -0700125import com.android.systemui.statusbar.notification.row.NotificationGuts;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400126import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
Rohan Shah20790b82018-07-02 17:21:04 -0700127import com.android.systemui.statusbar.notification.row.NotificationSnooze;
128import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700129import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900130import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400131import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
Selim Cinekc3fec682019-06-06 18:11:07 -0700132import com.android.systemui.statusbar.phone.KeyguardBypassController;
Jason Monke59dc402018-08-16 12:05:01 -0400133import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
Selim Cinekb5605e52015-02-20 18:21:41 +0100134import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monke59dc402018-08-16 12:05:01 -0400135import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700136import com.android.systemui.statusbar.phone.NotificationIconAreaController;
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500137import com.android.systemui.statusbar.phone.NotificationPanelViewController;
Selim Cinekaac93252015-04-14 20:04:12 -0700138import com.android.systemui.statusbar.phone.ScrimController;
Jason Monk297c04e2018-08-23 17:16:59 -0400139import com.android.systemui.statusbar.phone.ShadeController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800140import com.android.systemui.statusbar.phone.StatusBar;
Jason Monke59dc402018-08-16 12:05:01 -0400141import com.android.systemui.statusbar.policy.ConfigurationController;
142import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900143import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100144import com.android.systemui.statusbar.policy.ScrollAdapter;
Beverly482ad6a2019-11-06 16:57:05 -0500145import com.android.systemui.statusbar.policy.ZenModeController;
Gus Prevas33619af2018-10-26 15:40:27 -0400146import com.android.systemui.tuner.TunerService;
Ned Burnsd4a69f72019-06-19 19:49:19 -0400147import com.android.systemui.util.Assert;
Selim Cinek67b22602014-03-10 15:40:16 +0100148
Selim Cinek707e2072017-06-30 18:32:40 +0200149import java.io.FileDescriptor;
150import java.io.PrintWriter;
Ned Burns61269442019-05-02 18:27:23 -0400151import java.lang.annotation.Retention;
Selim Cinek572bbd42014-04-25 16:43:27 +0200152import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800153import java.util.Collections;
154import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200155import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800156import java.util.List;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800157import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200158
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500159import javax.inject.Inject;
160import javax.inject.Named;
161
Selim Cinek67b22602014-03-10 15:40:16 +0100162/**
163 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
164 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400165public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter,
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700166 NotificationListContainer, ConfigurationListener, Dumpable,
167 DynamicPrivacyController.Listener {
Selim Cinek67b22602014-03-10 15:40:16 +0100168
Selim Cinekd35c2792016-01-21 13:20:57 -0800169 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800170 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100171 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200172 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
173 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200174 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100175 /**
176 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
177 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800178 private static final int INVALID_POINTER = -1;
Gus Prevase2d6f042018-10-17 15:25:30 -0400179 /**
180 * The distance in pixels between sections when the sections are directly adjacent (no visible
181 * gap is drawn between them). In this case we don't want to round their corners.
182 */
183 private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
Selim Cinekc3fec682019-06-06 18:11:07 -0700184 private final KeyguardBypassController mKeyguardBypassController;
Selim Cinek5454a0d2019-07-30 17:14:50 -0700185 private final DynamicPrivacyController mDynamicPrivacyController;
186 private final SysuiStatusBarStateController mStatusbarStateController;
Selim Cinek67b22602014-03-10 15:40:16 +0100187
Selim Cinek1408eb52014-06-02 14:45:38 +0200188 private ExpandHelper mExpandHelper;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400189 private final NotificationSwipeHelper mSwipeHelper;
Selim Cinek67b22602014-03-10 15:40:16 +0100190 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800191 private final Paint mBackgroundPaint = new Paint();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700192 private final boolean mShouldDrawNotificationBackground;
Selim Cinekda330ff2019-06-11 15:41:28 -0700193 private boolean mHighPriorityBeforeSpeedBump;
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500194 private final boolean mAllowLongPress;
Gus Prevasa18dc572019-01-14 16:11:22 -0500195 private boolean mDismissRtl;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100196
Selim Cinekbc243a92016-09-27 16:35:13 -0700197 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100198 private int mOwnScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -0500199 private View mScrollAnchorView;
200 private int mScrollAnchorViewY;
Selim Cinek67b22602014-03-10 15:40:16 +0100201 private int mMaxLayoutHeight;
202
203 private VelocityTracker mVelocityTracker;
204 private OverScroller mScroller;
Gus Prevascdc98342019-01-14 14:29:44 -0500205 /** Last Y position reported by {@link #mScroller}, used to calculate scroll delta. */
206 private int mLastScrollerY;
207 /**
208 * True if the max position was set to a known position on the last call to {@link #mScroller}.
209 */
210 private boolean mIsScrollerBoundSet;
Ricky Waicd35def2016-05-03 11:07:07 +0100211 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100212 private int mTouchSlop;
213 private int mMinimumVelocity;
214 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100215 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200216 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100217 private boolean mIsBeingDragged;
218 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200219 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700220 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100221 private boolean mTouchIsClick;
222 private float mInitialTouchX;
223 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100224
Selim Cinek67b22602014-03-10 15:40:16 +0100225 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100226 private int mContentHeight;
Lucas Dupin60661a62018-04-12 10:50:13 -0700227 private int mIntrinsicContentHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100228 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100229 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800230 private int mIncreasedPaddingBetweenElements;
shawnlin8e4e92c2018-04-12 18:47:24 +0800231 private int mMaxTopPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200232 private int mTopPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700233 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700234 private int mBottomInset = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +0800235 private float mQsExpansionFraction;
Selim Cinek67b22602014-03-10 15:40:16 +0100236
237 /**
238 * The algorithm which calculates the properties for our children
239 */
Muyuan Li87798022016-04-07 17:51:25 -0700240 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100241
Selim Cinek281c2022016-10-13 19:14:43 -0700242 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100243 private NotificationGroupManager mGroupManager;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500244 private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700245 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500246 private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
247 private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200248 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700249 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
250 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200251 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200252 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200253 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800254 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200255
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200256 /**
257 * The raw amount of the overScroll on the top, which is not rubber-banded.
258 */
259 private float mOverScrolledTopPixels;
260
261 /**
262 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
263 */
264 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900265 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200266 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200267 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100268 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200269 private boolean mNeedsAnimation;
270 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200271 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200272 private boolean mHideSensitiveNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200273 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200274 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200275 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200276 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200277 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700278 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200279 private boolean mExpandingNotification;
280 private boolean mExpandedInThisMotion;
shawnlin8e4e92c2018-04-12 18:47:24 +0800281 private boolean mShouldShowShelfOnly;
Muyuan Li84b45612016-06-01 11:05:08 -0700282 protected boolean mScrollingEnabled;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400283 protected FooterView mFooterView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700284 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400285 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700286 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200287
288 /**
289 * Was the scroller scrolled to the top when the down motion was observed?
290 */
291 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200292 /**
293 * The minimal amount of over scroll which is needed in order to switch to the quick settings
294 * when over scrolling on a expanded card.
295 */
296 private float mMinTopOverScrollToEscape;
297 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700298 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200299 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200300 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700301 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200302 private boolean mNeedViewResizeAnimation;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500303 private ExpandableView mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700304 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700305
Selim Cinek1408eb52014-06-02 14:45:38 +0200306 /**
307 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
308 * This is needed to avoid scrolling too far after the notification was collapsed in the same
309 * motion.
310 */
311 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000312 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700313 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200314
315 /**
316 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
317 * animating.
318 */
319 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700320 private boolean mDisallowDismissInThisMotion;
Selim Cineka59ecc32015-04-07 10:51:49 -0700321 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700322 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200323 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200324 = new ViewTreeObserver.OnPreDrawListener() {
325 @Override
326 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400327 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200328 updateChildren();
329 mChildrenUpdateRequested = false;
330 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200331 return true;
332 }
333 };
Lucas Dupin28790c02020-01-30 14:18:41 -0800334 private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
335 @Override
336 public void onUserChanged(int userId) {
337 updateSensitiveness(false /* animated */);
338 }
339 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500340 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100341 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100342 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700343 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek9dd0d042018-05-14 18:12:42 -0700344 private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700345 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
346 = new HashSet<>();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900347 private HeadsUpManagerPhone mHeadsUpManager;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800348 private final NotificationRoundnessManager mRoundnessManager;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700349 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700350 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200351 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700352 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700353 private FalsingManager mFalsingManager;
Beverly482ad6a2019-11-06 16:57:05 -0500354 private final ZenModeController mZenController;
Selim Cinek6811d722016-01-19 17:53:12 -0800355 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700356 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800357 = new ViewTreeObserver.OnPreDrawListener() {
358 @Override
359 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700360 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800361 return true;
362 }
363 };
Evan Laird25f02752019-08-14 19:25:06 -0400364 private NotificationSection[] mSections;
Selim Cinek614576e2016-01-20 10:54:09 -0800365 private boolean mAnimateNextBackgroundTop;
Gus Prevase2d6f042018-10-17 15:25:30 -0400366 private boolean mAnimateNextBackgroundBottom;
367 private boolean mAnimateNextSectionBoundsChange;
Selim Cinekd35c2792016-01-21 13:20:57 -0800368 private int mBgColor;
369 private float mDimAmount;
370 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800371 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400372 private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
Selim Cinekd35c2792016-01-21 13:20:57 -0800373 @Override
374 public void onAnimationEnd(Animator animation) {
375 mDimAnimator = null;
376 }
377 };
378 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
379 = new ValueAnimator.AnimatorUpdateListener() {
380
381 @Override
382 public void onAnimationUpdate(ValueAnimator animation) {
383 setDimAmount((Float) animation.getAnimatedValue());
384 }
385 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700386 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800387 private boolean mContinuousShadowUpdate;
Selim Cinekae55d832019-02-22 17:43:43 -0800388 private boolean mContinuousBackgroundUpdate;
Selim Cinek33223572016-02-19 19:32:22 -0800389 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
390 = new ViewTreeObserver.OnPreDrawListener() {
391
392 @Override
393 public boolean onPreDraw() {
394 updateViewShadows();
395 return true;
396 }
397 };
Selim Cinekae55d832019-02-22 17:43:43 -0800398 private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> {
399 updateBackground();
400 return true;
401 };
Selim Cinek33223572016-02-19 19:32:22 -0800402 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
403 @Override
404 public int compare(ExpandableView view, ExpandableView otherView) {
405 float endY = view.getTranslationY() + view.getActualHeight();
406 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
407 if (endY < otherEndY) {
408 return -1;
409 } else if (endY > otherEndY) {
410 return 1;
411 } else {
412 // The two notifications end at the same location
413 return 0;
414 }
415 }
416 };
Lucas Dupin64e2f572019-03-21 14:21:14 -0700417 private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
418 @Override
419 public void getOutline(View view, Outline outline) {
Selim Cinek195dfc52019-05-30 19:35:05 -0700420 if (mAmbientState.isHiddenAtAll()) {
421 float xProgress = mHideXInterpolator.getInterpolation(
422 (1 - mLinearHideAmount) * mBackgroundXFactor);
Selim Cinek9ad240c2019-04-08 19:07:15 -0700423 outline.setRoundRect(mBackgroundAnimationRect,
424 MathUtils.lerp(mCornerRadius / 2.0f, mCornerRadius,
425 xProgress));
Selim Cinek33295a42019-07-26 19:49:08 -0700426 outline.setAlpha(1.0f - mAmbientState.getHideAmount());
Lucas Dupin64e2f572019-03-21 14:21:14 -0700427 } else {
428 ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
429 }
430 }
431 };
Selim Cinek25503252016-03-03 15:31:43 -0800432 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900433 private boolean mPulsing;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700434 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700435 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400436 private View mForcedScroll;
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200437
438 /**
Selim Cinek195dfc52019-05-30 19:35:05 -0700439 * @see #setHideAmount(float, float)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200440 */
Selim Cinek195dfc52019-05-30 19:35:05 -0700441 private float mInterpolatedHideAmount = 0f;
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200442
443 /**
Selim Cinek195dfc52019-05-30 19:35:05 -0700444 * @see #setHideAmount(float, float)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200445 */
Selim Cinek195dfc52019-05-30 19:35:05 -0700446 private float mLinearHideAmount = 0f;
Lucas Dupin439bd442018-06-12 15:05:28 -0700447
448 /**
449 * How fast the background scales in the X direction as a factor of the Y expansion.
450 */
451 private float mBackgroundXFactor = 1f;
Selim Cinek972123d2016-05-03 14:25:58 -0700452
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -0500453 private boolean mSwipingInProgress;
454
Lucas Dupine17ce522017-07-17 15:45:06 -0700455 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700456 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700457 private boolean mForwardScrollable;
458 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700459 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700460 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800461 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700462 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800463 private boolean mNoAmbient;
464 private final Rect mClipRect = new Rect();
465 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700466 private Rect mRequestedClipBounds;
467 private boolean mInHeadsUpPinnedMode;
468 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800469 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100470 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700471 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Gus Prevascdc98342019-01-14 14:29:44 -0500472 private Runnable mReflingAndAnimateScroll = () -> {
473 if (ANCHOR_SCROLLING) {
474 maybeReflingScroller();
475 }
476 animateScroll();
477 };
Selim Cinek0fe07392017-11-09 13:26:34 -0800478 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800479 private int mSidePaddings;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800480 private final Rect mBackgroundAnimationRect = new Rect();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800481 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
482 private int mHeadsUpInset;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700483 private HeadsUpAppearanceController mHeadsUpAppearanceController;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700484 private NotificationIconAreaController mIconAreaController;
Lucas Dupind236ee32019-10-08 15:33:59 -0700485 private final NotificationLockscreenUserManager mLockscreenUserManager;
Jason Monke59dc402018-08-16 12:05:01 -0400486 private final Rect mTmpRect = new Rect();
487 private final NotificationEntryManager mEntryManager =
488 Dependency.get(NotificationEntryManager.class);
489 private final IStatusBarService mBarService = IStatusBarService.Stub.asInterface(
490 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Will Brockmane718d582019-01-17 16:38:38 -0500491 @VisibleForTesting
492 protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Jason Monke59dc402018-08-16 12:05:01 -0400493 private final NotificationRemoteInputManager mRemoteInputManager =
494 Dependency.get(NotificationRemoteInputManager.class);
495 private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
496
497 private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
498 private final LockscreenGestureLogger mLockscreenGestureLogger =
499 Dependency.get(LockscreenGestureLogger.class);
500 private final VisualStabilityManager mVisualStabilityManager =
501 Dependency.get(VisualStabilityManager.class);
502 protected boolean mClearAllEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +0100503
Selim Cinek195dfc52019-05-30 19:35:05 -0700504 private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500505 private NotificationPanelViewController mNotificationPanelController;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400506
Lucas Dupind236ee32019-10-08 15:33:59 -0700507 private final NotificationGutsManager mNotificationGutsManager;
Ned Burns9eb06332019-04-23 16:02:12 -0400508 private final NotificationSectionsManager mSectionsManager;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700509 private boolean mAnimateBottomOnLayout;
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700510 private float mLastSentAppear;
511 private float mLastSentExpandedHeight;
Selim Cinekaae9ad62019-07-22 18:36:54 -0700512 private boolean mWillExpand;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400513
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500514 @Inject
515 public NotificationStackScrollLayout(
516 @Named(VIEW_CONTEXT) Context context,
517 AttributeSet attrs,
Lucas Dupin00be88f2019-01-03 17:50:52 -0800518 @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
Selim Cinek3d6ae232019-01-04 14:14:33 -0800519 NotificationRoundnessManager notificationRoundnessManager,
Ned Burns9eb06332019-04-23 16:02:12 -0400520 DynamicPrivacyController dynamicPrivacyController,
Dave Mankoff56fe9e42020-01-08 15:42:06 -0500521 SysuiStatusBarStateController statusBarStateController,
Selim Cinekc3fec682019-06-06 18:11:07 -0700522 HeadsUpManagerPhone headsUpManager,
Dave Mankoffc195ea82019-06-28 16:33:25 -0400523 KeyguardBypassController keyguardBypassController,
Evan Laird367b6142019-08-30 14:40:34 -0400524 FalsingManager falsingManager,
Lucas Dupind236ee32019-10-08 15:33:59 -0700525 NotificationLockscreenUserManager notificationLockscreenUserManager,
526 NotificationGutsManager notificationGutsManager,
Dave Mankoff56fe9e42020-01-08 15:42:06 -0500527 ZenModeController zenController,
528 NotificationSectionsManager notificationSectionsManager) {
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500529 super(context, attrs, 0, 0);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800530 Resources res = getResources();
531
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500532 mAllowLongPress = allowLongPress;
533
Selim Cinekc3fec682019-06-06 18:11:07 -0700534 mRoundnessManager = notificationRoundnessManager;
Gus Prevase2d6f042018-10-17 15:25:30 -0400535
Lucas Dupind236ee32019-10-08 15:33:59 -0700536 mLockscreenUserManager = notificationLockscreenUserManager;
537 mNotificationGutsManager = notificationGutsManager;
Selim Cinekc3fec682019-06-06 18:11:07 -0700538 mHeadsUpManager = headsUpManager;
539 mHeadsUpManager.addListener(mRoundnessManager);
540 mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
541 mKeyguardBypassController = keyguardBypassController;
Dave Mankoffc195ea82019-06-28 16:33:25 -0400542 mFalsingManager = falsingManager;
Beverly482ad6a2019-11-06 16:57:05 -0500543 mZenController = zenController;
Ned Burns61269442019-05-02 18:27:23 -0400544
Dave Mankoff56fe9e42020-01-08 15:42:06 -0500545 mSectionsManager = notificationSectionsManager;
546 mSectionsManager.initialize(this, LayoutInflater.from(context));
Ned Burns61269442019-05-02 18:27:23 -0400547 mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
548 // Leave the shade open if there will be other notifs left over to clear
549 final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
550 clearNotifications(ROWS_GENTLE, closeShade);
551 });
Dave Mankoff56fe9e42020-01-08 15:42:06 -0500552 mSections = mSectionsManager.createSectionsForBuckets();
Ned Burns61269442019-05-02 18:27:23 -0400553
Selim Cinekc3fec682019-06-06 18:11:07 -0700554 mAmbientState = new AmbientState(context, mSectionsManager, mHeadsUpManager);
Selim Cinekd35c2792016-01-21 13:20:57 -0800555 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800556 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
557 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400558 mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
Selim Cinek1cf41c12014-08-12 20:06:19 +0200559 minHeight, maxHeight);
560 mExpandHelper.setEventSource(this);
561 mExpandHelper.setScrollAdapter(this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400562 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
Dave Mankoffc195ea82019-06-28 16:33:25 -0400563 getContext(), mMenuEventListener, mFalsingManager);
Muyuan Li333a4fc2016-04-16 17:13:46 -0700564 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100565 initView(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800566 mShouldDrawNotificationBackground =
567 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700568 mFadeNotificationsOnDismiss =
569 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Selim Cinek29aab962018-02-27 17:05:45 -0800570 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
571 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700572 addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded);
Lucas Dupin28790c02020-01-30 14:18:41 -0800573 mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
Lucas Dupin64e2f572019-03-21 14:21:14 -0700574 setOutlineProvider(mOutlineProvider);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800575
Rohan Shah524cf7b2018-03-15 14:40:02 -0700576 // Blocking helper manager wants to know the expanded state, update as well.
577 NotificationBlockingHelperManager blockingHelperManager =
578 Dependency.get(NotificationBlockingHelperManager.class);
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700579 addOnExpandedHeightChangedListener((height, unused) -> {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700580 blockingHelperManager.setNotificationShadeExpanded(height);
581 });
582
Selim Cinek195dfc52019-05-30 19:35:05 -0700583 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
584 setWillNotDraw(!willDraw);
Lucas Dupind285cf02018-01-18 09:18:23 -0800585 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100586 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100587 mDebugPaint = new Paint();
588 mDebugPaint.setColor(0xffff0000);
589 mDebugPaint.setStrokeWidth(2);
590 mDebugPaint.setStyle(Paint.Style.STROKE);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500591 mDebugPaint.setTextSize(25f);
Selim Cinek67b22602014-03-10 15:40:16 +0100592 }
Jason Monke59dc402018-08-16 12:05:01 -0400593 mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
Gus Prevas33619af2018-10-26 15:40:27 -0400594
595 TunerService tunerService = Dependency.get(TunerService.class);
596 tunerService.addTunable((key, newValue) -> {
Selim Cinekda330ff2019-06-11 15:41:28 -0700597 if (key.equals(HIGH_PRIORITY)) {
598 mHighPriorityBeforeSpeedBump = "1".equals(newValue);
Gus Prevasa18dc572019-01-14 16:11:22 -0500599 } else if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) {
600 updateDismissRtlSetting("1".equals(newValue));
Gus Prevas33619af2018-10-26 15:40:27 -0400601 }
Selim Cinekda330ff2019-06-11 15:41:28 -0700602 }, HIGH_PRIORITY, Settings.Secure.NOTIFICATION_DISMISS_RTL);
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500603
604 mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
605 @Override
Beverlye018f0d2019-11-06 14:20:29 -0500606 public void onPreEntryUpdated(NotificationEntry entry) {
607 if (entry.rowExists() && !entry.getSbn().isClearable()) {
608 // If the row already exists, the user may have performed a dismiss action on
609 // the notification. Since it's not clearable we should snap it back.
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500610 snapViewIfNeeded(entry);
611 }
612 }
613 });
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700614 dynamicPrivacyController.addListener(this);
Selim Cinek5454a0d2019-07-30 17:14:50 -0700615 mDynamicPrivacyController = dynamicPrivacyController;
Dave Mankoff56fe9e42020-01-08 15:42:06 -0500616 mStatusbarStateController = statusBarStateController;
Jason Monke59dc402018-08-16 12:05:01 -0400617 }
618
Gus Prevasa18dc572019-01-14 16:11:22 -0500619 private void updateDismissRtlSetting(boolean dismissRtl) {
620 mDismissRtl = dismissRtl;
621 for (int i = 0; i < getChildCount(); i++) {
622 View child = getChildAt(i);
623 if (child instanceof ExpandableNotificationRow) {
624 ((ExpandableNotificationRow) child).setDismissRtl(dismissRtl);
625 }
626 }
627 }
628
Jason Monke59dc402018-08-16 12:05:01 -0400629 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400630 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400631 protected void onFinishInflate() {
632 super.onFinishInflate();
633
634 inflateEmptyShadeView();
635 inflateFooterView();
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400636 mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500637 if (mAllowLongPress) {
Steve Elliottff6c25e2019-05-30 17:38:52 -0400638 setLongPressListener(mNotificationGutsManager::openGuts);
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500639 }
Jason Monke59dc402018-08-16 12:05:01 -0400640 }
641
Selim Cinek34518f62019-02-28 19:41:18 -0800642 /**
643 * @return the height at which we will wake up when pulsing
644 */
Selim Cinekb0fada62019-06-17 19:03:59 -0700645 public float getWakeUpHeight() {
Selim Cinek3d6ae232019-01-04 14:14:33 -0800646 ActivatableNotificationView firstChild = getFirstChildWithBackground();
647 if (firstChild != null) {
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700648 if (mKeyguardBypassController.getBypassEnabled()) {
649 return firstChild.getHeadsUpHeightWithoutHeader();
650 } else {
651 return firstChild.getCollapsedHeight();
652 }
Selim Cinek3d6ae232019-01-04 14:14:33 -0800653 }
Selim Cinek34518f62019-02-28 19:41:18 -0800654 return 0f;
Selim Cinek3d6ae232019-01-04 14:14:33 -0800655 }
656
Jason Monke59dc402018-08-16 12:05:01 -0400657 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400658 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400659 public void onDensityOrFontScaleChanged() {
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800660 reinflateViews();
661 }
662
663 private void reinflateViews() {
Jason Monke59dc402018-08-16 12:05:01 -0400664 inflateFooterView();
665 inflateEmptyShadeView();
666 updateFooter();
Ned Burns2c74c2a2019-06-13 19:06:47 -0400667 mSectionsManager.reinflateViews(LayoutInflater.from(mContext));
Jason Monke59dc402018-08-16 12:05:01 -0400668 }
669
670 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400671 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400672 public void onThemeChanged() {
Lucas Dupin89233c42019-06-10 18:00:22 -0700673 final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
Jason Monke59dc402018-08-16 12:05:01 -0400674 updateDecorViews(useDarkText);
675
676 updateFooter();
677 }
678
Fabian Kozynskid254b192019-02-05 13:42:58 -0500679 @Override
680 public void onOverlayChanged() {
681 int newRadius = mContext.getResources().getDimensionPixelSize(
682 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
683 if (mCornerRadius != newRadius) {
684 mCornerRadius = newRadius;
685 invalidate();
686 }
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800687 reinflateViews();
Fabian Kozynskid254b192019-02-05 13:42:58 -0500688 }
689
Jason Monke59dc402018-08-16 12:05:01 -0400690 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400691 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400692 public void updateFooter() {
Ned Burns61269442019-05-02 18:27:23 -0400693 boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL);
Evan Laird181de622019-10-24 09:53:02 -0400694 boolean showFooterView = (showDismissView || mEntryManager.hasActiveNotifications())
Jason Monke59dc402018-08-16 12:05:01 -0400695 && mStatusBarState != StatusBarState.KEYGUARD
696 && !mRemoteInputManager.getController().isRemoteInputActive();
697
698 updateFooterView(showFooterView, showDismissView);
699 }
700
701 /**
702 * Return whether there are any clearable notifications
703 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400704 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -0400705 public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
Selim Cinek5454a0d2019-07-30 17:14:50 -0700706 if (mDynamicPrivacyController.isInLockedDownShade()) {
707 return false;
708 }
Jason Monke59dc402018-08-16 12:05:01 -0400709 int childCount = getChildCount();
710 for (int i = 0; i < childCount; i++) {
711 View child = getChildAt(i);
712 if (!(child instanceof ExpandableNotificationRow)) {
713 continue;
714 }
Ned Burns61269442019-05-02 18:27:23 -0400715 final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
716 if (row.canViewBeDismissed() && matchesSelection(row, selection)) {
Jason Monke59dc402018-08-16 12:05:01 -0400717 return true;
718 }
719 }
720 return false;
721 }
722
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400723 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400724 public RemoteInputController.Delegate createDelegate() {
Jason Monke59dc402018-08-16 12:05:01 -0400725 return new RemoteInputController.Delegate() {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500726 public void setRemoteInputActive(NotificationEntry entry,
Jason Monke59dc402018-08-16 12:05:01 -0400727 boolean remoteInputActive) {
728 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
Evan Laird94492852018-10-25 13:43:01 -0400729 entry.notifyHeightChanged(true /* needsAnimation */);
Jason Monke59dc402018-08-16 12:05:01 -0400730 updateFooter();
731 }
732
Ned Burnsf81c4c42019-01-07 14:10:43 -0500733 public void lockScrollTo(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400734 NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
Jason Monke59dc402018-08-16 12:05:01 -0400735 }
736
737 public void requestDisallowLongPressAndDismiss() {
738 requestDisallowLongPress();
739 requestDisallowDismiss();
740 }
741 };
Selim Cinek67b22602014-03-10 15:40:16 +0100742 }
743
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900744 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400745 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400746 protected void onAttachedToWindow() {
747 super.onAttachedToWindow();
Beverly8fdb5332019-02-04 14:29:49 -0500748 ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class))
749 .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
Jason Monke59dc402018-08-16 12:05:01 -0400750 Dependency.get(ConfigurationController.class).addCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400751 }
752
753 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400754 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400755 protected void onDetachedFromWindow() {
756 super.onDetachedFromWindow();
Jason Monkaf08c152018-12-04 11:12:39 -0500757 Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Jason Monke59dc402018-08-16 12:05:01 -0400758 Dependency.get(ConfigurationController.class).removeCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400759 }
760
761 @Override
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400762 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -0800763 public NotificationSwipeActionHelper getSwipeActionHelper() {
764 return mSwipeHelper;
765 }
766
Selim Cinek67b22602014-03-10 15:40:16 +0100767 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400768 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupinf03e7522018-06-25 16:21:13 -0700769 public void onUiModeChanged() {
770 mBgColor = mContext.getColor(R.color.notification_shade_background_color);
771 updateBackgroundDimming();
Selim Cinekab9c7b22018-12-11 18:15:47 -0800772 mShelf.onUiModeChanged();
Ned Burns9eb06332019-04-23 16:02:12 -0400773 mSectionsManager.onUiModeChanged();
Lucas Dupinf03e7522018-06-25 16:21:13 -0700774 }
775
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400776 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Selim Cinek67b22602014-03-10 15:40:16 +0100777 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800778 if (mShouldDrawNotificationBackground
Gus Prevase2d6f042018-10-17 15:25:30 -0400779 && (mSections[0].getCurrentBounds().top
Evan Laird25f02752019-08-14 19:25:06 -0400780 < mSections[mSections.length - 1].getCurrentBounds().bottom
Selim Cinekc1d9ab22019-05-21 18:08:30 -0700781 || mAmbientState.isDozing())) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800782 drawBackground(canvas);
Gus Prevas211181532018-12-13 14:49:33 -0500783 } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
784 drawHeadsUpBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700785 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800786
Selim Cinek67b22602014-03-10 15:40:16 +0100787 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800788 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100789 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800790 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100791 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200792 y = getHeight() - getEmptyBottomMargin();
793 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100794 }
795 }
796
Gus Prevas0fa58d62019-01-11 13:58:40 -0500797 @Override
798 public void draw(Canvas canvas) {
799 super.draw(canvas);
800
801 if (DEBUG && ANCHOR_SCROLLING) {
802 if (mScrollAnchorView instanceof ExpandableNotificationRow) {
803 canvas.drawRect(0,
804 mScrollAnchorView.getTranslationY(),
805 getWidth(),
806 mScrollAnchorView.getTranslationY()
807 + ((ExpandableNotificationRow) mScrollAnchorView).getActualHeight(),
808 mDebugPaint);
809 canvas.drawText(Integer.toString(mScrollAnchorViewY), getWidth() - 200,
810 mScrollAnchorView.getTranslationY() + 30, mDebugPaint);
811 int y = (int) mShelf.getTranslationY();
812 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
813 }
Gus Prevascdc98342019-01-14 14:29:44 -0500814 canvas.drawText(Integer.toString(getMaxNegativeScrollAmount()), getWidth() - 100,
Selim Cinekdb6d6df2019-05-22 20:39:59 -0700815 getTopPadding() + 30, mDebugPaint);
Gus Prevascdc98342019-01-14 14:29:44 -0500816 canvas.drawText(Integer.toString(getMaxPositiveScrollAmount()), getWidth() - 100,
817 getHeight() - 30, mDebugPaint);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500818 }
819 }
820
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400821 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupind285cf02018-01-18 09:18:23 -0800822 private void drawBackground(Canvas canvas) {
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700823 int lockScreenLeft = mSidePaddings;
824 int lockScreenRight = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -0400825 int lockScreenTop = mSections[0].getCurrentBounds().top;
Evan Laird25f02752019-08-14 19:25:06 -0400826 int lockScreenBottom = mSections[mSections.length - 1].getCurrentBounds().bottom;
Selim Cinek195dfc52019-05-30 19:35:05 -0700827 int hiddenLeft = getWidth() / 2;
828 int hiddenTop = mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800829
Selim Cinek195dfc52019-05-30 19:35:05 -0700830 float yProgress = 1 - mInterpolatedHideAmount;
831 float xProgress = mHideXInterpolator.getInterpolation(
832 (1 - mLinearHideAmount) * mBackgroundXFactor);
Lucas Dupin60661a62018-04-12 10:50:13 -0700833
Selim Cinek195dfc52019-05-30 19:35:05 -0700834 int left = (int) MathUtils.lerp(hiddenLeft, lockScreenLeft, xProgress);
835 int right = (int) MathUtils.lerp(hiddenLeft, lockScreenRight, xProgress);
836 int top = (int) MathUtils.lerp(hiddenTop, lockScreenTop, yProgress);
837 int bottom = (int) MathUtils.lerp(hiddenTop, lockScreenBottom, yProgress);
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700838 mBackgroundAnimationRect.set(
Gus Prevase2d6f042018-10-17 15:25:30 -0400839 left,
840 top,
841 right,
842 bottom);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200843
Gus Prevase2d6f042018-10-17 15:25:30 -0400844 int backgroundTopAnimationOffset = top - lockScreenTop;
845 // TODO(kprevas): this may not be necessary any more since we don't display the shelf in AOD
846 boolean anySectionHasVisibleChild = false;
847 for (NotificationSection section : mSections) {
848 if (section.getFirstVisibleChild() != null) {
849 anySectionHasVisibleChild = true;
850 break;
851 }
852 }
Selim Cinek5fedd812019-06-26 19:34:24 -0700853 boolean shouldDrawBackground;
854 if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) {
855 shouldDrawBackground = isPulseExpanding();
856 } else {
857 shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild;
858 }
859 if (shouldDrawBackground) {
Gus Prevase2d6f042018-10-17 15:25:30 -0400860 drawBackgroundRects(canvas, left, right, top, backgroundTopAnimationOffset);
Lucas Dupind285cf02018-01-18 09:18:23 -0800861 }
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700862
Lucas Dupin16cfe452018-02-08 13:14:50 -0800863 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800864 }
865
Gus Prevase2d6f042018-10-17 15:25:30 -0400866 /**
867 * Draws round rects for each background section.
868 *
869 * We want to draw a round rect for each background section as defined by {@link #mSections}.
870 * However, if two sections are directly adjacent with no gap between them (e.g. on the
871 * lockscreen where the shelf can appear directly below the high priority section, or while
872 * scrolling the shade so that the top of the shelf is right at the bottom of the high priority
873 * section), we don't want to round the adjacent corners.
874 *
875 * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we
876 * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect.
877 * This method tracks the top of each rect we need to draw, then iterates through the visible
878 * sections. If a section is not adjacent to the previous section, we draw the previous rect
879 * behind the sections we've accumulated up to that point, then start a new rect at the top of
880 * the current section. When we're done iterating we will always have one rect left to draw.
881 */
882 private void drawBackgroundRects(Canvas canvas, int left, int right, int top,
883 int animationYOffset) {
884 int backgroundRectTop = top;
885 int lastSectionBottom =
886 mSections[0].getCurrentBounds().bottom + animationYOffset;
Selim Cinek0acf4302019-06-11 12:43:03 -0700887 int currentLeft = left;
888 int currentRight = right;
Selim Cinekae55d832019-02-22 17:43:43 -0800889 boolean first = true;
Gus Prevase2d6f042018-10-17 15:25:30 -0400890 for (NotificationSection section : mSections) {
891 if (section.getFirstVisibleChild() == null) {
892 continue;
893 }
894 int sectionTop = section.getCurrentBounds().top + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800895 int ownLeft = Math.min(Math.max(left, section.getCurrentBounds().left), right);
Selim Cinekd0b48e32019-05-24 20:49:23 -0700896 int ownRight = Math.max(Math.min(right, section.getCurrentBounds().right), ownLeft);
Gus Prevase2d6f042018-10-17 15:25:30 -0400897 // If sections are directly adjacent to each other, we don't want to draw them
898 // as separate roundrects, as the rounded corners right next to each other look
899 // bad.
Selim Cinekae55d832019-02-22 17:43:43 -0800900 if (sectionTop - lastSectionBottom > DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX
Selim Cinek0acf4302019-06-11 12:43:03 -0700901 || ((currentLeft != ownLeft || currentRight != ownRight) && !first)) {
902 canvas.drawRoundRect(currentLeft,
Gus Prevase2d6f042018-10-17 15:25:30 -0400903 backgroundRectTop,
Selim Cinek0acf4302019-06-11 12:43:03 -0700904 currentRight,
Gus Prevase2d6f042018-10-17 15:25:30 -0400905 lastSectionBottom,
906 mCornerRadius, mCornerRadius, mBackgroundPaint);
907 backgroundRectTop = sectionTop;
908 }
Selim Cinek0acf4302019-06-11 12:43:03 -0700909 currentLeft = ownLeft;
910 currentRight = ownRight;
Gus Prevase2d6f042018-10-17 15:25:30 -0400911 lastSectionBottom =
912 section.getCurrentBounds().bottom + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800913 first = false;
Gus Prevase2d6f042018-10-17 15:25:30 -0400914 }
Selim Cinek0acf4302019-06-11 12:43:03 -0700915 canvas.drawRoundRect(currentLeft,
Gus Prevase2d6f042018-10-17 15:25:30 -0400916 backgroundRectTop,
Selim Cinek0acf4302019-06-11 12:43:03 -0700917 currentRight,
Gus Prevase2d6f042018-10-17 15:25:30 -0400918 lastSectionBottom,
919 mCornerRadius, mCornerRadius, mBackgroundPaint);
920 }
921
Gus Prevas211181532018-12-13 14:49:33 -0500922 private void drawHeadsUpBackground(Canvas canvas) {
923 int left = mSidePaddings;
924 int right = getWidth() - mSidePaddings;
925
926 float top = getHeight();
927 float bottom = 0;
928 int childCount = getChildCount();
929 for (int i = 0; i < childCount; i++) {
930 View child = getChildAt(i);
931 if (child.getVisibility() != View.GONE
932 && child instanceof ExpandableNotificationRow) {
933 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
shawnlin3a950c32019-05-15 20:06:10 +0800934 if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0
935 && row.getProvider().shouldShowGutsOnSnapOpen()) {
Gus Prevas211181532018-12-13 14:49:33 -0500936 top = Math.min(top, row.getTranslationY());
937 bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
938 }
939 }
940 }
941
942 if (top < bottom) {
943 canvas.drawRoundRect(
944 left, top, right, bottom,
945 mCornerRadius, mCornerRadius, mBackgroundPaint);
946 }
947 }
948
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400949 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd35c2792016-01-21 13:20:57 -0800950 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800951 // No need to update the background color if it's not being drawn.
952 if (!mShouldDrawNotificationBackground) {
953 return;
954 }
955
Lucas Dupinb561eda2018-04-09 17:25:04 -0700956 // Interpolate between semi-transparent notification panel background color
957 // and white AOD separator.
Lucas Dupinf03e7522018-06-25 16:21:13 -0700958 float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */,
Selim Cinek195dfc52019-05-30 19:35:05 -0700959 mLinearHideAmount);
Felka Chang97bc8d52019-05-29 02:51:15 +0800960 int color = ColorUtils.blendARGB(mBgColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800961
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100962 if (mCachedBackgroundColor != color) {
963 mCachedBackgroundColor = color;
964 mBackgroundPaint.setColor(color);
965 invalidate();
966 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800967 }
968
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400969 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +0100970 private void initView(Context context) {
971 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100972 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200973 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100974 final ViewConfiguration configuration = ViewConfiguration.get(context);
975 mTouchSlop = configuration.getScaledTouchSlop();
976 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
977 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100978 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700979
980 Resources res = context.getResources();
981 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800982 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700983 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700984 mPaddingBetweenElements = Math.max(1,
985 res.getDimensionPixelSize(R.dimen.notification_divider_height));
986 mIncreasedPaddingBetweenElements =
987 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
988 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200989 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800990 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700991 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800992 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700993 mMinInteractionHeight = res.getDimensionPixelSize(
994 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800995 mCornerRadius = res.getDimensionPixelSize(
996 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800997 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
998 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200999 }
1000
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001001 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekaef92ef2014-06-06 18:06:04 +02001002 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -07001003 notifyHeightChangeListener(view, false /* needsAnimation */);
1004 }
1005
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001006 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -07001007 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +02001008 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -07001009 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +02001010 }
Selim Cinek67b22602014-03-10 15:40:16 +01001011 }
1012
Selim Cinekb0fada62019-06-17 19:03:59 -07001013 public boolean isPulseExpanding() {
1014 return mAmbientState.isPulseExpanding();
1015 }
1016
Selim Cinek67b22602014-03-10 15:40:16 +01001017 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001018 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001019 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1020 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -08001021
1022 int width = MeasureSpec.getSize(widthMeasureSpec);
1023 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
1024 MeasureSpec.getMode(widthMeasureSpec));
Evan Lairdeb7dbd52018-06-28 13:17:25 -04001025 // Don't constrain the height of the children so we know how big they'd like to be
1026 int childHeightSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
1027 MeasureSpec.UNSPECIFIED);
1028
Selim Cinekfa760d42016-05-10 15:50:53 -04001029 // We need to measure all children even the GONE ones, such that the heights are calculated
1030 // correctly as they are used to calculate how many we can fit on the screen.
1031 final int size = getChildCount();
1032 for (int i = 0; i < size; i++) {
Evan Lairdeb7dbd52018-06-28 13:17:25 -04001033 measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -04001034 }
Selim Cinek67b22602014-03-10 15:40:16 +01001035 }
1036
1037 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001038 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001039 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +01001040 // we layout all our children centered on the top
1041 float centerX = getWidth() / 2.0f;
1042 for (int i = 0; i < getChildCount(); i++) {
1043 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -04001044 // We need to layout all children even the GONE ones, such that the heights are
1045 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +01001046 float width = child.getMeasuredWidth();
1047 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +01001048 child.layout((int) (centerX - width / 2.0f),
1049 0,
1050 (int) (centerX + width / 2.0f),
1051 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +01001052 }
Jorim Jaggi1d480692014-05-20 19:41:58 +02001053 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +01001054 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02001055 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +02001056 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -08001057 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -07001058 updateAlgorithmLayoutMinHeight();
Selim Cinek33295a42019-07-26 19:49:08 -07001059 updateOwnTranslationZ();
Selim Cinek67b22602014-03-10 15:40:16 +01001060 }
1061
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001062 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5bc852a2015-12-21 12:19:09 -08001063 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
1064 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +02001065 mNeedViewResizeAnimation = true;
1066 mNeedsAnimation = true;
1067 }
Selim Cineka5e211b2014-08-11 17:35:48 +02001068 }
1069
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001070 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekdb167372016-11-17 15:41:17 -08001071 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
1072 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001073 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +02001074 }
1075
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001076 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001077 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001078 public void setChildLocationsChangedListener(
1079 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001080 mListener = listener;
1081 }
1082
Selim Cineka7d4f822016-12-06 14:34:47 -08001083 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001084 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001085 public boolean isInVisibleLocation(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001086 ExpandableNotificationRow row = entry.getRow();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001087 ExpandableViewState childViewState = row.getViewState();
1088
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001089 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001090 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001091 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +01001092 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001093 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +01001094 }
Selim Cineka7d4f822016-12-06 14:34:47 -08001095 if (row.getVisibility() != View.VISIBLE) {
1096 return false;
1097 }
1098 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001099 }
1100
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001101 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinek67b22602014-03-10 15:40:16 +01001102 private void setMaxLayoutHeight(int maxLayoutHeight) {
1103 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -07001104 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001105 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +01001106 }
1107
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001108 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001109 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001110 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -07001111 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001112 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +01001113 }
1114
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001115 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinekbc243a92016-09-27 16:35:13 -07001116 private void updateAlgorithmLayoutMinHeight() {
shawnlinc3457912018-05-15 16:39:56 +08001117 mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001118 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -07001119 }
1120
Selim Cinek67b22602014-03-10 15:40:16 +01001121 /**
1122 * Updates the children views according to the stack scroll algorithm. Call this whenever
1123 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
1124 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001125 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001126 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -08001127 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -08001128 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
1129 ? 0
1130 : mScroller.getCurrVelocity());
Gus Prevas0fa58d62019-01-11 13:58:40 -05001131 if (ANCHOR_SCROLLING) {
1132 mAmbientState.setAnchorViewIndex(indexOfChild(mScrollAnchorView));
1133 mAmbientState.setAnchorViewY(mScrollAnchorViewY);
1134 } else {
1135 mAmbientState.setScrollY(mOwnScrollY);
1136 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001137 mStackScrollAlgorithm.resetViewStates(mAmbientState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001138 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02001139 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +01001140 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +02001141 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +01001142 }
1143 }
1144
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001145 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc383fd02016-10-21 15:31:26 -07001146 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -07001147 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001148 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -07001149 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
1150 updateBackground();
1151 }
Selim Cinekc383fd02016-10-21 15:31:26 -07001152 }
1153
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001154 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001155 private void updateClippingToTopRoundedCorner() {
Arthur Hungc0ef5652018-05-22 14:00:42 +08001156 Float clipStart = (float) mTopPadding
Jason Monke59dc402018-08-16 12:05:01 -04001157 + mStackTranslation
1158 + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001159 Float clipEnd = clipStart + mCornerRadius;
1160 boolean first = true;
1161 for (int i = 0; i < getChildCount(); i++) {
1162 ExpandableView child = (ExpandableView) getChildAt(i);
1163 if (child.getVisibility() == GONE) {
1164 continue;
1165 }
1166 float start = child.getTranslationY();
Arthur Hungc0ef5652018-05-22 14:00:42 +08001167 float end = start + child.getActualHeight();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001168 boolean clip = clipStart > start && clipStart < end
1169 || clipEnd >= start && clipEnd <= end;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001170 clip &= !(first && isScrolledToTop());
Selim Cinekeccf4942018-05-30 09:55:36 -07001171 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
1172 : ExpandableView.NO_ROUNDNESS);
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001173 first = false;
1174 }
1175 }
1176
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001177 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek3776fe02016-02-04 13:32:43 -08001178 private void updateScrollStateForAddedChildren() {
1179 if (mChildrenToAddAnimated.isEmpty()) {
1180 return;
1181 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001182 if (!ANCHOR_SCROLLING) {
1183 for (int i = 0; i < getChildCount(); i++) {
1184 ExpandableView child = (ExpandableView) getChildAt(i);
1185 if (mChildrenToAddAnimated.contains(child)) {
1186 int startingPosition = getPositionInLinearLayout(child);
1187 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
1188 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
1189 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
1190 int childHeight = getIntrinsicHeight(child) + padding;
1191 if (startingPosition < mOwnScrollY) {
1192 // This child starts off screen, so let's keep it offscreen to keep the
1193 // others visible
Selim Cinek3776fe02016-02-04 13:32:43 -08001194
Gus Prevas0fa58d62019-01-11 13:58:40 -05001195 setOwnScrollY(mOwnScrollY + childHeight);
1196 }
Selim Cinek3776fe02016-02-04 13:32:43 -08001197 }
1198 }
1199 }
1200 clampScrollPosition();
1201 }
1202
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001203 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001204 private void updateForcedScroll() {
1205 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
1206 || !mForcedScroll.isAttachedToWindow())) {
1207 mForcedScroll = null;
1208 }
1209 if (mForcedScroll != null) {
1210 ExpandableView expandableView = (ExpandableView) mForcedScroll;
1211 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
1212 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -07001213 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -04001214
Gus Prevas0fa58d62019-01-11 13:58:40 -05001215 if (ANCHOR_SCROLLING) {
1216 // TODO
1217 } else {
1218 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -07001219
Gus Prevas0fa58d62019-01-11 13:58:40 -05001220 // Only apply the scroll if we're scrolling the view upwards, or the view is so
1221 // far up that it is not visible anymore.
1222 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1223 setOwnScrollY(targetScroll);
1224 }
Adrian Roos181385c2016-05-05 17:45:44 -04001225 }
1226 }
1227 }
1228
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001229 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek319bdc42014-05-01 23:01:58 +02001230 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +02001231 if (!mChildrenUpdateRequested) {
1232 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
1233 mChildrenUpdateRequested = true;
1234 invalidate();
1235 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001236 }
1237
Robert Snoeberger168949a2019-04-18 09:39:42 -04001238 /**
1239 * Returns best effort count of visible notifications.
1240 */
1241 public int getVisibleNotificationCount() {
1242 int count = 0;
1243 for (int i = 0; i < getChildCount(); i++) {
1244 final View child = getChildAt(i);
1245 if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
1246 count++;
1247 }
1248 }
1249 return count;
1250 }
1251
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001252 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001253 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +02001254 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +01001255 }
1256
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001257 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekf7a14c02014-07-07 14:01:46 +02001258 private void clampScrollPosition() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001259 if (ANCHOR_SCROLLING) {
1260 // TODO
1261 } else {
1262 int scrollRange = getScrollRange();
1263 if (scrollRange < mOwnScrollY) {
1264 setOwnScrollY(scrollRange);
1265 }
Selim Cinek67b22602014-03-10 15:40:16 +01001266 }
1267 }
1268
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001269 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001270 public int getTopPadding() {
1271 return mTopPadding;
1272 }
1273
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001274 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001275 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupin00be88f2019-01-03 17:50:52 -08001276 if (mTopPadding != topPadding) {
1277 mTopPadding = topPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001278 updateAlgorithmHeightAndPadding();
1279 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +02001280 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001281 mTopPaddingNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04001282 mNeedsAnimation = true;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001283 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001284 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -07001285 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001286 }
1287 }
1288
1289 /**
Selim Cinekbc243a92016-09-27 16:35:13 -07001290 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001291 *
Selim Cinekbc243a92016-09-27 16:35:13 -07001292 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001293 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001294 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbc243a92016-09-27 16:35:13 -07001295 public void setExpandedHeight(float height) {
1296 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001297 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001298 int minExpansionHeight = getMinExpansionHeight();
1299 if (height < minExpansionHeight) {
1300 mClipRect.left = 0;
1301 mClipRect.right = getWidth();
1302 mClipRect.top = 0;
1303 mClipRect.bottom = (int) height;
1304 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001305 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001306 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001307 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001308 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001309 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -07001310 float translationY;
1311 float appearEndPosition = getAppearEndPosition();
1312 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001313 float appearFraction = 1.0f;
shawnlin5be1f7c2018-05-21 20:50:54 +08001314 boolean appearing = height < appearEndPosition;
1315 mAmbientState.setAppearing(appearing);
1316 if (!appearing) {
Selim Cinekbc243a92016-09-27 16:35:13 -07001317 translationY = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +08001318 if (mShouldShowShelfOnly) {
1319 stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
1320 } else if (mQsExpanded) {
1321 int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
1322 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
1323 if (stackStartPosition <= stackEndPosition) {
1324 stackHeight = stackEndPosition;
1325 } else {
1326 stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
1327 stackEndPosition, mQsExpansionFraction);
1328 }
1329 } else {
1330 stackHeight = (int) height;
1331 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001332 } else {
Selim Cinekc7e4cb52019-06-20 15:41:45 -07001333 appearFraction = calculateAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -07001334 if (appearFraction >= 0) {
1335 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
1336 appearFraction);
1337 } else {
1338 // This may happen when pushing up a heads up. We linearly push it up from the
1339 // start
1340 translationY = height - appearStartPosition + getExpandTranslationStart();
1341 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001342 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001343 stackHeight =
1344 getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001345 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
1346 } else {
1347 stackHeight = (int) (height - translationY);
1348 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001349 }
1350 if (stackHeight != mCurrentStackHeight) {
1351 mCurrentStackHeight = stackHeight;
1352 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +02001353 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001354 }
Selim Cinek94c2d822016-07-13 18:50:04 -07001355 setStackTranslation(translationY);
Selim Cinekc7e4cb52019-06-20 15:41:45 -07001356 notifyAppearChangedListeners();
1357 }
1358
1359 private void notifyAppearChangedListeners() {
1360 float appear;
1361 float expandAmount;
1362 if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) {
1363 appear = calculateAppearFractionBypass();
1364 expandAmount = getPulseHeight();
1365 } else {
1366 appear = MathUtils.saturate(calculateAppearFraction(mExpandedHeight));
1367 expandAmount = mExpandedHeight;
1368 }
1369 if (appear != mLastSentAppear || expandAmount != mLastSentExpandedHeight) {
1370 mLastSentAppear = appear;
1371 mLastSentExpandedHeight = expandAmount;
1372 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
1373 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
1374 listener.accept(expandAmount, appear);
1375 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001376 }
Selim Cinekcafa87f2016-10-26 17:00:17 -07001377 }
1378
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001379 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001380 private void setRequestedClipBounds(Rect clipRect) {
1381 mRequestedClipBounds = clipRect;
1382 updateClipping();
1383 }
1384
Lucas Dupin60661a62018-04-12 10:50:13 -07001385 /**
1386 * Return the height of the content ignoring the footer.
1387 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001388 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -07001389 public int getIntrinsicContentHeight() {
1390 return mIntrinsicContentHeight;
1391 }
1392
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001393 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001394 public void updateClipping() {
1395 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
1396 && !mHeadsUpAnimatingAway;
Lucas Dupin64e2f572019-03-21 14:21:14 -07001397 boolean clipToOutline = false;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001398 if (mIsClipped != clipped) {
1399 mIsClipped = clipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001400 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08001401
Selim Cinek195dfc52019-05-30 19:35:05 -07001402 if (mAmbientState.isHiddenAtAll()) {
Lucas Dupin64e2f572019-03-21 14:21:14 -07001403 clipToOutline = true;
1404 invalidateOutline();
Selim Cinek195dfc52019-05-30 19:35:05 -07001405 if (isFullyHidden()) {
1406 setClipBounds(null);
1407 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08001408 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001409 setClipBounds(mRequestedClipBounds);
1410 } else {
1411 setClipBounds(null);
1412 }
Lucas Dupin64e2f572019-03-21 14:21:14 -07001413
1414 setClipToOutline(clipToOutline);
Selim Cinek94c2d822016-07-13 18:50:04 -07001415 }
1416
1417 /**
1418 * @return The translation at the beginning when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001419 * Measured relative to the resting position.
Selim Cinek94c2d822016-07-13 18:50:04 -07001420 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001421 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001422 private float getExpandTranslationStart() {
Selim Cinek083f2142018-11-06 16:32:23 -08001423 return -mTopPadding + getMinExpansionHeight() - mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001424 }
1425
1426 /**
1427 * @return the position from where the appear transition starts when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001428 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001429 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001430 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001431 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001432 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001433 return mHeadsUpInset
1434 + getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -07001435 }
Selim Cinek48ff9b42016-11-09 19:31:51 -08001436 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001437 }
1438
1439 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001440 * @return the height of the top heads up notification when pinned. This is different from the
Jason Monke59dc402018-08-16 12:05:01 -04001441 * intrinsic height, which also includes whether the notification is system expanded and
1442 * is mainly used when dragging down from a heads up notification.
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001443 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001444 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001445 private int getTopHeadsUpPinnedHeight() {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001446 NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001447 if (topEntry == null) {
1448 return 0;
1449 }
Evan Laird94492852018-10-25 13:43:01 -04001450 ExpandableNotificationRow row = topEntry.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001451 if (row.isChildInGroup()) {
Ned Burns1c2b85a42019-11-14 15:37:03 -05001452 final NotificationEntry groupSummary =
1453 mGroupManager.getGroupSummary(row.getEntry().getSbn());
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001454 if (groupSummary != null) {
Evan Laird94492852018-10-25 13:43:01 -04001455 row = groupSummary.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001456 }
1457 }
1458 return row.getPinnedHeadsUpHeight();
1459 }
1460
1461 /**
Selim Cinek94c2d822016-07-13 18:50:04 -07001462 * @return the position from where the appear transition ends when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001463 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001464 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001465 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001466 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -07001467 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -07001468 int notGoneChildCount = getNotGoneChildCount();
Julia Reynolds34f14962018-05-03 12:40:20 +00001469 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001470 if (isHeadsUpTransition()
Selim Cinekc1d9ab22019-05-21 18:08:30 -07001471 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDozing())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001472 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +01001473 } else {
1474 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001475 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1476 appearPosition += mShelf.getIntrinsicHeight();
1477 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001478 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001479 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001480 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001481 }
1482 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001483 }
1484
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001485 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001486 private boolean isHeadsUpTransition() {
Gus Prevase2d6f042018-10-17 15:25:30 -04001487 NotificationSection firstVisibleSection = getFirstVisibleSection();
1488 return mTrackingHeadsUp && firstVisibleSection != null
Selim Cinek459aee32019-02-20 11:18:56 -08001489 && firstVisibleSection.getFirstVisibleChild().isAboveShelf();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001490 }
1491
Selim Cinek94c2d822016-07-13 18:50:04 -07001492 /**
1493 * @param height the height of the panel
1494 * @return the fraction of the appear animation that has been performed
1495 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001496 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekc7e4cb52019-06-20 15:41:45 -07001497 public float calculateAppearFraction(float height) {
Selim Cinek94c2d822016-07-13 18:50:04 -07001498 float appearEndPosition = getAppearEndPosition();
1499 float appearStartPosition = getAppearStartPosition();
1500 return (height - appearStartPosition)
1501 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001502 }
1503
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001504 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001505 public float getStackTranslation() {
1506 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001507 }
1508
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001509 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001510 private void setStackTranslation(float stackTranslation) {
1511 if (stackTranslation != mStackTranslation) {
1512 mStackTranslation = stackTranslation;
1513 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001514 requestChildrenUpdate();
1515 }
Selim Cinek67b22602014-03-10 15:40:16 +01001516 }
1517
1518 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001519 * 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 +01001520 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1521 *
1522 * @return either the layout height or the externally defined height, whichever is smaller
1523 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001524 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek343e6e22014-04-11 21:23:30 +02001525 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001526 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1527 }
1528
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001529 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek816c8e42015-11-19 12:00:45 -08001530 public int getFirstItemMinHeight() {
1531 final ExpandableView firstChild = getFirstChildNotGone();
1532 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001533 }
1534
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001535 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Jason Monk16ac3772016-02-10 15:39:21 -05001536 public void setQsContainer(ViewGroup qsContainer) {
1537 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001538 }
1539
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001540 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek684a4422015-04-15 16:18:39 -07001541 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001542 if (v instanceof ExpandableNotificationRow) {
1543 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001544 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001545 }
1546 return false;
1547 }
1548
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001549 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cineka59ecc32015-04-07 10:51:49 -07001550 private boolean isHeadsUp(View v) {
1551 if (v instanceof ExpandableNotificationRow) {
1552 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1553 return row.isHeadsUp();
1554 }
1555 return false;
1556 }
1557
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001558 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001559 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1560 getLocationOnScreen(mTempInt2);
1561 float localTouchY = touchY - mTempInt2[1];
1562
1563 ExpandableView closestChild = null;
1564 float minDist = Float.MAX_VALUE;
1565
1566 // find the view closest to the location, accounting for GONE views
1567 final int count = getChildCount();
1568 for (int childIdx = 0; childIdx < count; childIdx++) {
1569 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1570 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001571 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001572 continue;
1573 }
1574 float childTop = slidingChild.getTranslationY();
1575 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001576 float bottom = childTop + slidingChild.getActualHeight()
1577 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001578
1579 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1580 if (dist < minDist) {
1581 closestChild = slidingChild;
1582 minDist = dist;
1583 }
1584 }
1585 return closestChild;
1586 }
1587
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001588 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
1589 private ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001590 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1591
1592 }
1593
1594 /**
1595 * Get the child at a certain screen location.
1596 *
Jason Monke59dc402018-08-16 12:05:01 -04001597 * @param touchX the x coordinate
1598 * @param touchY the y coordinate
Selim Cinek34ed7c02017-09-08 15:03:12 -07001599 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1600 * @return the child at the given location.
1601 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001602 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek34ed7c02017-09-08 15:03:12 -07001603 private ExpandableView getChildAtPosition(float touchX, float touchY,
1604 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001605 // find the view under the pointer, accounting for GONE views
1606 final int count = getChildCount();
1607 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001608 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001609 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001610 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001611 continue;
1612 }
Selim Cinek89faff12014-06-19 16:29:04 -07001613 float childTop = slidingChild.getTranslationY();
1614 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001615 float bottom = childTop + slidingChild.getActualHeight()
1616 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001617
1618 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1619 // camera affordance).
1620 int left = 0;
1621 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001622
Selim Cinek34ed7c02017-09-08 15:03:12 -07001623 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001624 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001625 if (slidingChild instanceof ExpandableNotificationRow) {
1626 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Ned Burnsf81c4c42019-01-07 14:10:43 -05001627 NotificationEntry entry = row.getEntry();
Selim Cinek131c1e22015-05-11 19:04:49 -07001628 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Evan Laird94492852018-10-25 13:43:01 -04001629 && mHeadsUpManager.getTopEntry().getRow() != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001630 && mGroupManager.getGroupSummary(
Ned Burns00b4b2d2019-10-17 22:09:27 -04001631 mHeadsUpManager.getTopEntry().getSbn())
Evan Laird94492852018-10-25 13:43:01 -04001632 != entry) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001633 continue;
1634 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001635 return row.getViewAtPosition(touchY - childTop);
1636 }
Selim Cinek67b22602014-03-10 15:40:16 +01001637 return slidingChild;
1638 }
1639 }
1640 return null;
1641 }
1642
Selim Cinek3d6ae232019-01-04 14:14:33 -08001643 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001644 getLocationOnScreen(mTempInt2);
1645 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001646 }
1647
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001648 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001649 public void setScrollingEnabled(boolean enable) {
1650 mScrollingEnabled = enable;
1651 }
1652
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001653 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001654 public void lockScrollTo(View v) {
1655 if (mForcedScroll == v) {
1656 return;
1657 }
1658 mForcedScroll = v;
1659 scrollTo(v);
1660 }
1661
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001662 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001663 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001664 ExpandableView expandableView = (ExpandableView) v;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001665 if (ANCHOR_SCROLLING) {
1666 // TODO
1667 } else {
1668 int positionInLinearLayout = getPositionInLinearLayout(v);
1669 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1670 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001671
Gus Prevas0fa58d62019-01-11 13:58:40 -05001672 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1673 // that it is not visible anymore.
1674 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1675 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1676 mDontReportNextOverScroll = true;
1677 animateScroll();
1678 return true;
1679 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001680 }
Ricky Waicd35def2016-05-03 11:07:07 +01001681 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001682 }
1683
Adrian Roos181385c2016-05-05 17:45:44 -04001684 /**
1685 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
Jason Monke59dc402018-08-16 12:05:01 -04001686 * the IME.
Adrian Roos181385c2016-05-05 17:45:44 -04001687 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001688 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos181385c2016-05-05 17:45:44 -04001689 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1690 return positionInLinearLayout + v.getIntrinsicHeight() +
felkachang529bfe62018-07-04 12:51:44 +08001691 getImeInset() - getHeight()
1692 + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
Adrian Roos181385c2016-05-05 17:45:44 -04001693 }
1694
Adrian Roos5153d4a2016-03-22 10:01:56 -07001695 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001696 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001697 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001698 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001699
Gus Prevas0fa58d62019-01-11 13:58:40 -05001700 if (ANCHOR_SCROLLING) {
1701 // TODO
1702 } else {
1703 int range = getScrollRange();
1704 if (mOwnScrollY > range) {
1705 // HACK: We're repeatedly getting staggered insets here while the IME is
1706 // animating away. To work around that we'll wait until things have settled.
1707 removeCallbacks(mReclamp);
1708 postDelayed(mReclamp, 50);
1709 } else if (mForcedScroll != null) {
1710 // The scroll was requested before we got the actual inset - in case we need
1711 // to scroll up some more do so now.
1712 scrollTo(mForcedScroll);
1713 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001714 }
1715 return insets;
1716 }
1717
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001718 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001719 private Runnable mReclamp = new Runnable() {
1720 @Override
1721 public void run() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001722 if (ANCHOR_SCROLLING) {
1723 // TODO
1724 } else {
1725 int range = getScrollRange();
1726 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1727 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001728 mDontReportNextOverScroll = true;
1729 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001730 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001731 }
1732 };
1733
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001734 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
1735 public void setExpandingEnabled(boolean enable) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001736 mExpandHelper.setEnabled(enable);
1737 }
1738
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001739 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001740 private boolean isScrollingEnabled() {
1741 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001742 }
1743
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001744 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07001745 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001746 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001747 }
1748
Selim Cinek67b22602014-03-10 15:40:16 +01001749 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001750 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001751 protected void onConfigurationChanged(Configuration newConfig) {
1752 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001753 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001754 float densityScale = getResources().getDisplayMetrics().density;
1755 mSwipeHelper.setDensityScale(densityScale);
1756 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1757 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1758 initView(getContext());
1759 }
1760
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001761 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dan Sandlereceda3d2014-07-21 15:35:01 -04001762 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001763 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1764 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001765 }
1766
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001767 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001768 private void snapViewIfNeeded(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001769 ExpandableNotificationRow child = entry.getRow();
dongwan0605.kim30637e42016-03-02 17:16:47 +09001770 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001771 // If the child is showing the notification menu snap to that
Evan Lairde55c6012019-03-13 12:54:37 -04001772 if (child.getProvider() != null) {
1773 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
1774 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
1775 }
dongwan0605.kim30637e42016-03-02 17:16:47 +09001776 }
1777
Selim Cinek67b22602014-03-10 15:40:16 +01001778 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001779 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001780 public ViewGroup getViewParentForNotification(NotificationEntry entry) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001781 return this;
1782 }
1783
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001784 /**
1785 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1786 *
1787 * @param deltaY The amount to scroll upwards, has to be positive.
1788 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001789 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001790 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001791 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001792 private float overScrollUp(int deltaY, int range) {
1793 deltaY = Math.max(deltaY, 0);
1794 float currentTopAmount = getCurrentOverScrollAmount(true);
1795 float newTopAmount = currentTopAmount - deltaY;
1796 if (currentTopAmount > 0) {
1797 setOverScrollAmount(newTopAmount, true /* onTop */,
1798 false /* animate */);
1799 }
1800 // Top overScroll might not grab all scrolling motion,
1801 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001802 if (ANCHOR_SCROLLING) {
1803 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1804 // TODO: once we're recycling this will need to check the adapter position of the child
1805 ExpandableView lastRow = getLastRowNotGone();
Gus Prevascdc98342019-01-14 14:29:44 -05001806 if (lastRow != null && !lastRow.isInShelf()) {
1807 float distanceToMax = Math.max(0, getMaxPositiveScrollAmount());
1808 if (scrollAmount > distanceToMax) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001809 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1810 // We overScroll on the bottom
1811 setOverScrolledPixels(currentBottomPixels + (scrollAmount - distanceToMax),
1812 false /* onTop */,
1813 false /* animate */);
1814 mScrollAnchorViewY -= distanceToMax;
1815 scrollAmount = 0f;
1816 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001817 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001818 return scrollAmount;
1819 } else {
1820 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1821 float newScrollY = mOwnScrollY + scrollAmount;
1822 if (newScrollY > range) {
1823 if (!mExpandedInThisMotion) {
1824 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1825 // We overScroll on the bottom
1826 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1827 false /* onTop */,
1828 false /* animate */);
1829 }
1830 setOwnScrollY(range);
1831 scrollAmount = 0.0f;
1832 }
1833 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001834 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001835 }
1836
1837 /**
1838 * Perform a scroll downward and adapt the overscroll amounts accordingly
1839 *
1840 * @param deltaY The amount to scroll downwards, has to be negative.
1841 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001842 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001843 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001844 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001845 private float overScrollDown(int deltaY) {
1846 deltaY = Math.min(deltaY, 0);
1847 float currentBottomAmount = getCurrentOverScrollAmount(false);
1848 float newBottomAmount = currentBottomAmount + deltaY;
1849 if (currentBottomAmount > 0) {
1850 setOverScrollAmount(newBottomAmount, false /* onTop */,
1851 false /* animate */);
1852 }
1853 // Bottom overScroll might not grab all scrolling motion,
1854 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001855 if (ANCHOR_SCROLLING) {
1856 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1857 // TODO: once we're recycling this will need to check the adapter position of the child
1858 ExpandableView firstChild = getFirstChildNotGone();
1859 float top = firstChild.getTranslationY();
1860 float distanceToTop = mScrollAnchorView.getTranslationY() - top - mScrollAnchorViewY;
1861 if (distanceToTop < -scrollAmount) {
1862 float currentTopPixels = getCurrentOverScrolledPixels(true);
1863 // We overScroll on the top
1864 setOverScrolledPixels(currentTopPixels + (-scrollAmount - distanceToTop),
1865 true /* onTop */,
1866 false /* animate */);
1867 mScrollAnchorView = firstChild;
1868 mScrollAnchorViewY = 0;
1869 scrollAmount = 0f;
1870 }
1871 return scrollAmount;
1872 } else {
1873 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1874 float newScrollY = mOwnScrollY + scrollAmount;
1875 if (newScrollY < 0) {
1876 float currentTopPixels = getCurrentOverScrolledPixels(true);
1877 // We overScroll on the top
1878 setOverScrolledPixels(currentTopPixels - newScrollY,
1879 true /* onTop */,
1880 false /* animate */);
1881 setOwnScrollY(0);
1882 scrollAmount = 0.0f;
1883 }
1884 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001885 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001886 }
1887
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001888 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001889 private void initVelocityTrackerIfNotExists() {
1890 if (mVelocityTracker == null) {
1891 mVelocityTracker = VelocityTracker.obtain();
1892 }
1893 }
1894
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001895 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001896 private void recycleVelocityTracker() {
1897 if (mVelocityTracker != null) {
1898 mVelocityTracker.recycle();
1899 mVelocityTracker = null;
1900 }
1901 }
1902
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001903 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001904 private void initOrResetVelocityTracker() {
1905 if (mVelocityTracker == null) {
1906 mVelocityTracker = VelocityTracker.obtain();
1907 } else {
1908 mVelocityTracker.clear();
1909 }
1910 }
1911
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001912 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001913 public void setFinishScrollingCallback(Runnable runnable) {
1914 mFinishScrollingCallback = runnable;
1915 }
1916
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001917 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9212de82017-02-06 16:04:28 -08001918 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001919 if (mScroller.computeScrollOffset()) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001920 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001921 int oldY = mLastScrollerY;
1922 int y = mScroller.getCurrY();
1923 int deltaY = y - oldY;
1924 if (deltaY != 0) {
1925 int maxNegativeScrollAmount = getMaxNegativeScrollAmount();
1926 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
1927 if ((maxNegativeScrollAmount < 0 && deltaY < maxNegativeScrollAmount)
1928 || (maxPositiveScrollAmount > 0 && deltaY > maxPositiveScrollAmount)) {
1929 // This frame takes us into overscroll, so set the max overscroll based on
1930 // the current velocity
1931 setMaxOverScrollFromCurrentVelocity();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001932 }
Gus Prevascdc98342019-01-14 14:29:44 -05001933 customOverScrollBy(deltaY, oldY, 0, (int) mMaxOverScroll);
1934 mLastScrollerY = y;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001935 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001936 } else {
1937 int oldY = mOwnScrollY;
1938 int y = mScroller.getCurrY();
Selim Cinek67b22602014-03-10 15:40:16 +01001939
Gus Prevas0fa58d62019-01-11 13:58:40 -05001940 if (oldY != y) {
1941 int range = getScrollRange();
1942 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
Gus Prevascdc98342019-01-14 14:29:44 -05001943 // This frame takes us into overscroll, so set the max overscroll based on
1944 // the current velocity
1945 setMaxOverScrollFromCurrentVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +01001946 }
Selim Cinek67b22602014-03-10 15:40:16 +01001947
Gus Prevas0fa58d62019-01-11 13:58:40 -05001948 if (mDontClampNextScroll) {
1949 range = Math.max(range, oldY);
1950 }
1951 customOverScrollBy(y - oldY, oldY, range,
1952 (int) (mMaxOverScroll));
Adrian Roos5153d4a2016-03-22 10:01:56 -07001953 }
Selim Cinek67b22602014-03-10 15:40:16 +01001954 }
1955
Gus Prevascdc98342019-01-14 14:29:44 -05001956 postOnAnimation(mReflingAndAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001957 } else {
1958 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001959 if (mFinishScrollingCallback != null) {
1960 mFinishScrollingCallback.run();
1961 }
Selim Cinek67b22602014-03-10 15:40:16 +01001962 }
1963 }
1964
Gus Prevascdc98342019-01-14 14:29:44 -05001965 private void setMaxOverScrollFromCurrentVelocity() {
1966 float currVelocity = mScroller.getCurrVelocity();
1967 if (currVelocity >= mMinimumVelocity) {
1968 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
Selim Cinek4195dd02014-05-19 18:16:14 +02001969 }
Gus Prevascdc98342019-01-14 14:29:44 -05001970 }
Selim Cinek4195dd02014-05-19 18:16:14 +02001971
Gus Prevascdc98342019-01-14 14:29:44 -05001972 /**
1973 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
1974 * would cause us to exceed the provided maximum overscroll, springs back instead.
1975 *
1976 * This method performs the determination of whether we're exceeding the overscroll and clamps
1977 * the scroll amount if so. The actual scrolling/overscrolling happens in
1978 * {@link #onCustomOverScrolled(int, boolean)} (absolute scrolling) or
1979 * {@link #onCustomOverScrolledBy(int, boolean)} (anchor scrolling).
1980 *
1981 * @param deltaY The (signed) number of pixels to scroll.
1982 * @param scrollY The current scroll position (absolute scrolling only).
1983 * @param scrollRangeY The maximum allowable scroll position (absolute scrolling only).
1984 * @param maxOverScrollY The current (unsigned) limit on number of pixels to overscroll by.
1985 */
Selim Cinek4195dd02014-05-19 18:16:14 +02001986 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevascdc98342019-01-14 14:29:44 -05001987 private void customOverScrollBy(int deltaY, int scrollY, int scrollRangeY, int maxOverScrollY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001988 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001989 boolean clampedY = false;
1990 if (deltaY < 0) {
1991 int maxScrollAmount = getMaxNegativeScrollAmount();
1992 if (maxScrollAmount > Integer.MIN_VALUE) {
1993 maxScrollAmount -= maxOverScrollY;
1994 if (deltaY < maxScrollAmount) {
1995 deltaY = maxScrollAmount;
1996 clampedY = true;
1997 }
1998 }
1999 } else {
2000 int maxScrollAmount = getMaxPositiveScrollAmount();
2001 if (maxScrollAmount < Integer.MAX_VALUE) {
2002 maxScrollAmount += maxOverScrollY;
2003 if (deltaY > maxScrollAmount) {
2004 deltaY = maxScrollAmount;
2005 clampedY = true;
2006 }
2007 }
2008 }
2009 onCustomOverScrolledBy(deltaY, clampedY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05002010 } else {
2011 int newScrollY = scrollY + deltaY;
2012 final int top = -maxOverScrollY;
2013 final int bottom = maxOverScrollY + scrollRangeY;
Selim Cinek4195dd02014-05-19 18:16:14 +02002014
Gus Prevas0fa58d62019-01-11 13:58:40 -05002015 boolean clampedY = false;
2016 if (newScrollY > bottom) {
2017 newScrollY = bottom;
2018 clampedY = true;
2019 } else if (newScrollY < top) {
2020 newScrollY = top;
2021 clampedY = true;
2022 }
Selim Cinek4195dd02014-05-19 18:16:14 +02002023
Gus Prevas0fa58d62019-01-11 13:58:40 -05002024 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02002025 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002026 }
2027
2028 /**
2029 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
2030 * overscroll effect based on numPixels. By default this will also cancel animations on the
2031 * same overScroll edge.
2032 *
2033 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
2034 * the rubber-banding logic.
Jason Monke59dc402018-08-16 12:05:01 -04002035 * @param onTop Should the effect be applied on top of the scroller.
2036 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002037 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002038 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002039 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07002040 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002041 }
2042
2043 /**
2044 * Set the effective overScroll amount which will be directly reflected in the layout.
2045 * By default this will also cancel animations on the same overScroll edge.
2046 *
Jason Monke59dc402018-08-16 12:05:01 -04002047 * @param amount The amount to overScroll by.
2048 * @param onTop Should the effect be applied on top of the scroller.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002049 * @param animate Should an animation be performed.
2050 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002051
2052 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002053 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
2054 setOverScrollAmount(amount, onTop, animate, true);
2055 }
2056
2057 /**
2058 * Set the effective overScroll amount which will be directly reflected in the layout.
2059 *
Jason Monke59dc402018-08-16 12:05:01 -04002060 * @param amount The amount to overScroll by.
2061 * @param onTop Should the effect be applied on top of the scroller.
2062 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002063 * @param cancelAnimators Should running animations be cancelled.
2064 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002065 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002066 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
2067 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002068 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
2069 }
2070
2071 /**
2072 * Set the effective overScroll amount which will be directly reflected in the layout.
2073 *
Jason Monke59dc402018-08-16 12:05:01 -04002074 * @param amount The amount to overScroll by.
2075 * @param onTop Should the effect be applied on top of the scroller.
2076 * @param animate Should an animation be performed.
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002077 * @param cancelAnimators Should running animations be cancelled.
Jason Monke59dc402018-08-16 12:05:01 -04002078 * @param isRubberbanded The value which will be passed to
2079 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002080 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002081 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002082 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
2083 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002084 if (cancelAnimators) {
2085 mStateAnimator.cancelOverScrollAnimators(onTop);
2086 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002087 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002088 }
2089
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002090 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002091 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
2092 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002093 amount = Math.max(0, amount);
2094 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002095 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002096 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07002097 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002098 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002099 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002100 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002101 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002102 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002103 }
2104 }
2105
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002106 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002107 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002108 mExpandHelper.onlyObserveMovements(amount > 1.0f);
2109 if (mDontReportNextOverScroll) {
2110 mDontReportNextOverScroll = false;
2111 return;
2112 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02002113 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002114 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002115 }
2116 }
2117
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002118 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi290600a2014-05-30 17:02:20 +02002119 public void setOverscrollTopChangedListener(
2120 OnOverscrollTopChangedListener overscrollTopChangedListener) {
2121 mOverscrollTopChangedListener = overscrollTopChangedListener;
2122 }
2123
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002124 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002125 public float getCurrentOverScrollAmount(boolean top) {
2126 return mAmbientState.getOverScrollAmount(top);
2127 }
2128
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002129 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002130 public float getCurrentOverScrolledPixels(boolean top) {
Jason Monke59dc402018-08-16 12:05:01 -04002131 return top ? mOverScrolledTopPixels : mOverScrolledBottomPixels;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002132 }
2133
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002134 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002135 private void setOverScrolledPixels(float amount, boolean onTop) {
2136 if (onTop) {
2137 mOverScrolledTopPixels = amount;
2138 } else {
2139 mOverScrolledBottomPixels = amount;
2140 }
2141 }
2142
Gus Prevascdc98342019-01-14 14:29:44 -05002143 /**
2144 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
2145 * would cause us to exceed the provided maximum overscroll, springs back instead.
2146 *
2147 * @param deltaY The (signed) number of pixels to scroll.
2148 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2149 * the overscroll limit.
2150 */
2151 private void onCustomOverScrolledBy(int deltaY, boolean clampedY) {
2152 assert ANCHOR_SCROLLING;
Gus Prevas0fa58d62019-01-11 13:58:40 -05002153 mScrollAnchorViewY -= deltaY;
2154 // Treat animating scrolls differently; see #computeScroll() for why.
2155 if (!mScroller.isFinished()) {
Gus Prevascdc98342019-01-14 14:29:44 -05002156 if (clampedY) {
2157 springBack();
2158 } else {
2159 float overScrollTop = getCurrentOverScrollAmount(true /* top */);
2160 if (isScrolledToTop() && mScrollAnchorViewY > 0) {
2161 notifyOverscrollTopListener(mScrollAnchorViewY,
2162 isRubberbanded(true /* onTop */));
2163 } else {
2164 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true /* onTop */));
2165 }
2166 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002167 }
2168 updateScrollAnchor();
2169 updateOnScrollChange();
2170 }
2171
Gus Prevascdc98342019-01-14 14:29:44 -05002172 /**
2173 * Scrolls to the given position, overscrolling if needed. If called during a fling and the
2174 * position exceeds the provided maximum overscroll, springs back instead.
2175 *
2176 * @param scrollY The target scroll position.
2177 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2178 * the overscroll limit.
2179 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002180 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek9212de82017-02-06 16:04:28 -08002181 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002182 assert !ANCHOR_SCROLLING;
Selim Cinek67b22602014-03-10 15:40:16 +01002183 // Treat animating scrolls differently; see #computeScroll() for why.
2184 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07002185 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002186 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002187 springBack();
2188 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02002189 float overScrollTop = getCurrentOverScrollAmount(true);
2190 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002191 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002192 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002193 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002194 }
Selim Cinek67b22602014-03-10 15:40:16 +01002195 }
Selim Cinek67b22602014-03-10 15:40:16 +01002196 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08002197 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002198 }
2199 }
2200
Gus Prevascdc98342019-01-14 14:29:44 -05002201 /**
2202 * Springs back from an overscroll by stopping the {@link #mScroller} and animating the
2203 * overscroll amount back to zero.
2204 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002205 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002206 private void springBack() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002207 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002208 boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0;
2209 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
2210 boolean overscrolledBottom = maxPositiveScrollAmount < 0;
2211 if (overScrolledTop || overscrolledBottom) {
2212 float newAmount;
2213 if (overScrolledTop) {
2214 newAmount = mScrollAnchorViewY;
2215 mScrollAnchorViewY = 0;
2216 mDontReportNextOverScroll = true;
2217 } else {
2218 newAmount = -maxPositiveScrollAmount;
2219 mScrollAnchorViewY -= maxPositiveScrollAmount;
2220 }
2221 setOverScrollAmount(newAmount, overScrolledTop, false);
2222 setOverScrollAmount(0.0f, overScrolledTop, true);
2223 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002224 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002225 } else {
2226 int scrollRange = getScrollRange();
2227 boolean overScrolledTop = mOwnScrollY <= 0;
2228 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2229 if (overScrolledTop || overScrolledBottom) {
2230 boolean onTop;
2231 float newAmount;
2232 if (overScrolledTop) {
2233 onTop = true;
2234 newAmount = -mOwnScrollY;
2235 setOwnScrollY(0);
2236 mDontReportNextOverScroll = true;
2237 } else {
2238 onTop = false;
2239 newAmount = mOwnScrollY - scrollRange;
2240 setOwnScrollY(scrollRange);
2241 }
2242 setOverScrollAmount(newAmount, onTop, false);
2243 setOverScrollAmount(0.0f, onTop, true);
2244 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002245 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002246 }
2247 }
2248
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002249 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek67b22602014-03-10 15:40:16 +01002250 private int getScrollRange() {
felkachang529bfe62018-07-04 12:51:44 +08002251 // In current design, it only use the top HUN to treat all of HUNs
2252 // although there are more than one HUNs
2253 int contentHeight = mContentHeight;
2254 if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
2255 contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
2256 }
2257 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002258 int imeInset = getImeInset();
felkachang529bfe62018-07-04 12:51:44 +08002259 scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
Selim Cineka424c502016-04-05 13:07:54 -07002260 return scrollRange;
2261 }
2262
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002263 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cineka424c502016-04-05 13:07:54 -07002264 private int getImeInset() {
2265 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002266 }
2267
Selim Cinek343e6e22014-04-11 21:23:30 +02002268 /**
2269 * @return the first child which has visibility unequal to GONE
2270 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002271 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb55386d2015-12-16 17:26:49 -08002272 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002273 int childCount = getChildCount();
2274 for (int i = 0; i < childCount; i++) {
2275 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002276 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002277 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002278 }
2279 }
2280 return null;
2281 }
2282
Selim Cinek4a1ac842014-05-01 15:51:58 +02002283 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002284 * @return the child before the given view which has visibility unequal to GONE
2285 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002286 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002287 public ExpandableView getViewBeforeView(ExpandableView view) {
2288 ExpandableView previousView = null;
2289 int childCount = getChildCount();
2290 for (int i = 0; i < childCount; i++) {
2291 View child = getChildAt(i);
2292 if (child == view) {
2293 return previousView;
2294 }
2295 if (child.getVisibility() != View.GONE) {
2296 previousView = (ExpandableView) child;
2297 }
2298 }
2299 return null;
2300 }
2301
2302 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002303 * @return The first child which has visibility unequal to GONE which is currently below the
Jason Monke59dc402018-08-16 12:05:01 -04002304 * given translationY or equal to it.
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002305 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002306 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekef8c2252017-02-10 14:52:18 -08002307 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002308 int childCount = getChildCount();
2309 for (int i = 0; i < childCount; i++) {
2310 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002311 if (child.getVisibility() == View.GONE) {
2312 continue;
2313 }
2314 float rowTranslation = child.getTranslationY();
2315 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002316 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002317 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2318 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2319 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2320 List<ExpandableNotificationRow> notificationChildren =
2321 row.getNotificationChildren();
2322 for (int childIndex = 0; childIndex < notificationChildren.size();
2323 childIndex++) {
2324 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2325 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2326 return rowChild;
2327 }
2328 }
2329 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002330 }
2331 }
2332 return null;
2333 }
2334
2335 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002336 * @return the last child which has visibility unequal to GONE
2337 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002338 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002339 public ExpandableView getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002340 int childCount = getChildCount();
2341 for (int i = childCount - 1; i >= 0; i--) {
2342 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002343 if (child.getVisibility() != View.GONE && child != mShelf) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002344 return (ExpandableView) child;
Selim Cinek4a1ac842014-05-01 15:51:58 +02002345 }
2346 }
2347 return null;
2348 }
2349
Gus Prevas0fa58d62019-01-11 13:58:40 -05002350 private ExpandableNotificationRow getLastRowNotGone() {
2351 int childCount = getChildCount();
2352 for (int i = childCount - 1; i >= 0; i--) {
2353 View child = getChildAt(i);
2354 if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
2355 return (ExpandableNotificationRow) child;
2356 }
2357 }
2358 return null;
2359 }
2360
Jorim Jaggi069cd032014-05-15 03:09:01 +02002361 /**
2362 * @return the number of children which have visibility unequal to GONE
2363 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002364 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi069cd032014-05-15 03:09:01 +02002365 public int getNotGoneChildCount() {
2366 int childCount = getChildCount();
2367 int count = 0;
2368 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002369 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002370 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002371 count++;
2372 }
2373 }
2374 return count;
2375 }
2376
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002377 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01002378 private void updateContentHeight() {
2379 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002380 float previousPaddingRequest = mPaddingBetweenElements;
2381 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002382 int numShownItems = 0;
2383 boolean finish = false;
Selim Cinek5040f2e2019-02-14 18:22:42 -08002384 int maxDisplayedNotifications = mMaxDisplayedNotifications;
Adrian Roos7a9551a2017-01-11 12:27:49 -08002385
Selim Cinek67b22602014-03-10 15:40:16 +01002386 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002387 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002388 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002389 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002390 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002391 boolean limitReached = maxDisplayedNotifications != -1
2392 && numShownItems >= maxDisplayedNotifications;
Selim Cinek5040f2e2019-02-14 18:22:42 -08002393 if (limitReached) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002394 expandableView = mShelf;
2395 finish = true;
2396 }
Selim Cinek42357e02016-02-24 18:48:01 -08002397 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002398 float padding;
2399 if (increasedPaddingAmount >= 0.0f) {
2400 padding = (int) NotificationUtils.interpolate(
2401 previousPaddingRequest,
2402 mIncreasedPaddingBetweenElements,
2403 increasedPaddingAmount);
2404 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002405 mPaddingBetweenElements,
2406 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002407 increasedPaddingAmount);
2408 } else {
2409 int ownPadding = (int) NotificationUtils.interpolate(
2410 0,
2411 mPaddingBetweenElements,
2412 1.0f + increasedPaddingAmount);
2413 if (previousPaddingAmount > 0.0f) {
2414 padding = (int) NotificationUtils.interpolate(
2415 ownPadding,
2416 mIncreasedPaddingBetweenElements,
2417 previousPaddingAmount);
2418 } else {
2419 padding = ownPadding;
2420 }
2421 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002422 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002423 if (height != 0) {
2424 height += padding;
2425 }
2426 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002427 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002428 numShownItems++;
2429 if (finish) {
2430 break;
2431 }
Selim Cinek67b22602014-03-10 15:40:16 +01002432 }
2433 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002434 mIntrinsicContentHeight = height;
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002435
Selim Cinekdb6d6df2019-05-22 20:39:59 -07002436 // The topPadding can be bigger than the regular padding when qs is expanded, in that
2437 // state the maxPanelHeight and the contentHeight should be bigger
2438 mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002439 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002440 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002441 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002442 }
2443
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002444 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002445 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbe2c4432017-05-30 12:11:09 -07002446 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002447 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002448 }
2449
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002450 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07002451 private void updateScrollability() {
Riddle Hsu065c01c2018-05-10 23:14:19 +08002452 boolean scrollable = !mQsExpanded && getScrollRange() > 0;
Selim Cinekc22fff62016-05-20 12:44:30 -07002453 if (scrollable != mScrollable) {
2454 mScrollable = scrollable;
2455 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002456 updateForwardAndBackwardScrollability();
2457 }
2458 }
2459
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002460 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef406062016-09-29 17:33:13 -07002461 private void updateForwardAndBackwardScrollability() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002462 boolean forwardScrollable = mScrollable && !isScrolledToBottom();
2463 boolean backwardsScrollable = mScrollable && !isScrolledToTop();
Selim Cinekef406062016-09-29 17:33:13 -07002464 boolean changed = forwardScrollable != mForwardScrollable
2465 || backwardsScrollable != mBackwardScrollable;
2466 mForwardScrollable = forwardScrollable;
2467 mBackwardScrollable = backwardsScrollable;
2468 if (changed) {
2469 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002470 }
Selim Cinek67b22602014-03-10 15:40:16 +01002471 }
2472
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002473 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002474 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002475 // No need to update the background color if it's not being drawn.
Selim Cinek195dfc52019-05-30 19:35:05 -07002476 if (!mShouldDrawNotificationBackground) {
Selim Cinek6811d722016-01-19 17:53:12 -08002477 return;
2478 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002479
Selim Cinek6811d722016-01-19 17:53:12 -08002480 updateBackgroundBounds();
Gus Prevase2d6f042018-10-17 15:25:30 -04002481 if (didSectionBoundsChange()) {
2482 boolean animate = mAnimateNextSectionBoundsChange || mAnimateNextBackgroundTop
2483 || mAnimateNextBackgroundBottom || areSectionBoundsAnimating();
Selim Cinek54680902016-10-19 16:49:44 -07002484 if (!isExpanded()) {
2485 abortBackgroundAnimators();
2486 animate = false;
2487 }
2488 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002489 startBackgroundAnimation();
2490 } else {
Gus Prevase2d6f042018-10-17 15:25:30 -04002491 for (NotificationSection section : mSections) {
2492 section.resetCurrentBounds();
2493 }
Lucas Dupin90a38dd2018-09-05 09:37:37 -07002494 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002495 }
2496 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002497 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002498 }
Selim Cinek614576e2016-01-20 10:54:09 -08002499 mAnimateNextBackgroundTop = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04002500 mAnimateNextBackgroundBottom = false;
2501 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08002502 }
2503
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002504 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek54680902016-10-19 16:49:44 -07002505 private void abortBackgroundAnimators() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002506 for (NotificationSection section : mSections) {
2507 section.cancelAnimators();
Selim Cinek54680902016-10-19 16:49:44 -07002508 }
2509 }
2510
Gus Prevase2d6f042018-10-17 15:25:30 -04002511 private boolean didSectionBoundsChange() {
2512 for (NotificationSection section : mSections) {
2513 if (section.didBoundsChange()) {
2514 return true;
2515 }
2516 }
2517 return false;
2518 }
2519
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002520 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevase2d6f042018-10-17 15:25:30 -04002521 private boolean areSectionBoundsAnimating() {
2522 for (NotificationSection section : mSections) {
2523 if (section.areBoundsAnimating()) {
2524 return true;
2525 }
2526 }
2527 return false;
Selim Cinek614576e2016-01-20 10:54:09 -08002528 }
2529
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002530 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08002531 private void startBackgroundAnimation() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002532 // TODO(kprevas): do we still need separate fields for top/bottom?
2533 // or can each section manage its own animation state?
2534 NotificationSection firstVisibleSection = getFirstVisibleSection();
2535 NotificationSection lastVisibleSection = getLastVisibleSection();
2536 for (NotificationSection section : mSections) {
2537 section.startBackgroundAnimation(
2538 section == firstVisibleSection
2539 ? mAnimateNextBackgroundTop
2540 : mAnimateNextSectionBoundsChange,
2541 section == lastVisibleSection
2542 ? mAnimateNextBackgroundBottom
2543 : mAnimateNextSectionBoundsChange);
Selim Cinek614576e2016-01-20 10:54:09 -08002544 }
Selim Cinek6811d722016-01-19 17:53:12 -08002545 }
2546
2547 /**
2548 * Update the background bounds to the new desired bounds
2549 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002550 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002551 private void updateBackgroundBounds() {
shawnlin27716722019-04-17 20:09:45 +08002552 int left = mSidePaddings;
2553 int right = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -04002554 for (NotificationSection section : mSections) {
2555 section.getBounds().left = left;
2556 section.getBounds().right = right;
2557 }
shawnlin3a2a2e22018-05-04 17:09:50 +08002558
Selim Cinek614576e2016-01-20 10:54:09 -08002559 if (!mIsExpanded) {
Gus Prevase2d6f042018-10-17 15:25:30 -04002560 for (NotificationSection section : mSections) {
2561 section.getBounds().top = 0;
2562 section.getBounds().bottom = 0;
2563 }
Selim Cinek1791f502016-10-07 17:38:03 -04002564 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002565 }
Selim Cinek893a9df2019-07-19 17:30:16 -07002566 int minTopPosition;
Gus Prevase2d6f042018-10-17 15:25:30 -04002567 NotificationSection lastSection = getLastVisibleSection();
Selim Cinekc3fec682019-06-06 18:11:07 -07002568 boolean onKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
2569 if (!onKeyguard) {
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002570 minTopPosition = (int) (mTopPadding + mStackTranslation);
2571 } else if (lastSection == null) {
2572 minTopPosition = mTopPadding;
Selim Cinek893a9df2019-07-19 17:30:16 -07002573 } else {
2574 // The first sections could be empty while there could still be elements in later
2575 // sections. The position of these first few sections is determined by the position of
2576 // the first visible section.
2577 NotificationSection firstVisibleSection = getFirstVisibleSection();
2578 firstVisibleSection.updateBounds(0 /* minTopPosition*/, 0 /* minBottomPosition */,
2579 false /* shiftPulsingWithFirst */);
2580 minTopPosition = firstVisibleSection.getBounds().top;
Selim Cinek3776fe02016-02-04 13:32:43 -08002581 }
Selim Cinekc3fec682019-06-06 18:11:07 -07002582 boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1
2583 && (mAmbientState.isDozing()
2584 || (mKeyguardBypassController.getBypassEnabled() && onKeyguard));
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002585 for (NotificationSection section : mSections) {
2586 int minBottomPosition = minTopPosition;
2587 if (section == lastSection) {
2588 // We need to make sure the section goes all the way to the shelf
Selim Cinek3a1d2742019-03-11 18:38:29 -07002589 minBottomPosition = (int) (ViewState.getFinalTranslationY(mShelf)
2590 + mShelf.getIntrinsicHeight());
Gus Prevase83700cb2018-12-14 11:42:51 -05002591 }
Selim Cinekae55d832019-02-22 17:43:43 -08002592 minTopPosition = section.updateBounds(minTopPosition, minBottomPosition,
2593 shiftPulsingWithFirst);
2594 shiftPulsingWithFirst = false;
Gus Prevase83700cb2018-12-14 11:42:51 -05002595 }
Selim Cinek614576e2016-01-20 10:54:09 -08002596 }
2597
Gus Prevase2d6f042018-10-17 15:25:30 -04002598 private NotificationSection getFirstVisibleSection() {
2599 for (NotificationSection section : mSections) {
2600 if (section.getFirstVisibleChild() != null) {
2601 return section;
2602 }
2603 }
2604 return null;
2605 }
2606
2607 private NotificationSection getLastVisibleSection() {
2608 for (int i = mSections.length - 1; i >= 0; i--) {
2609 NotificationSection section = mSections[i];
2610 if (section.getLastVisibleChild() != null) {
2611 return section;
2612 }
2613 }
2614 return null;
2615 }
2616
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002617 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002618 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002619 int childCount = getChildCount();
2620 for (int i = childCount - 1; i >= 0; i--) {
2621 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002622 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2623 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002624 return (ActivatableNotificationView) child;
2625 }
2626 }
2627 return null;
2628 }
2629
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002630 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002631 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002632 int childCount = getChildCount();
2633 for (int i = 0; i < childCount; i++) {
2634 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002635 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2636 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002637 return (ActivatableNotificationView) child;
2638 }
2639 }
2640 return null;
2641 }
2642
Evan Laird25f02752019-08-14 19:25:06 -04002643 //TODO: We shouldn't have to generate this list every time
2644 private List<ActivatableNotificationView> getChildrenWithBackground() {
2645 ArrayList<ActivatableNotificationView> children = new ArrayList<>();
2646 int childCount = getChildCount();
2647 for (int i = 0; i < childCount; i++) {
2648 View child = getChildAt(i);
2649 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2650 && child != mShelf) {
2651 children.add((ActivatableNotificationView) child);
2652 }
2653 }
2654
2655 return children;
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 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002845 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekdb6d6df2019-05-22 20:39:59 -07002846 public void updateTopPadding(float qsHeight, boolean animate) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002847 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002848 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002849 if (topPadding + minStackHeight > getHeight()) {
2850 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002851 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002852 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002853 }
Selim Cinekb0fada62019-06-17 19:03:59 -07002854 setTopPadding(topPadding, animate && !mKeyguardBypassController.getBypassEnabled());
Selim Cinekbc243a92016-09-27 16:35:13 -07002855 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002856 }
2857
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002858 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
shawnlin8e4e92c2018-04-12 18:47:24 +08002859 public void setMaxTopPadding(int maxTopPadding) {
2860 mMaxTopPadding = maxTopPadding;
2861 }
2862
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002863 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002864 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002865 if (isHeadsUpTransition()) {
2866 return getTopHeadsUpPinnedHeight();
2867 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002868 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002869 }
2870
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002871 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002872 public float getTopPaddingOverflow() {
2873 return mTopPaddingOverflow;
2874 }
2875
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002876 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002877 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002878 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002879 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002880 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002881 int shelfHeight = 0;
Gus Prevase2d6f042018-10-17 15:25:30 -04002882 if (getLastVisibleSection() != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002883 shelfHeight = mShelf.getIntrinsicHeight();
2884 }
2885 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002886 }
2887
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002888 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02002889 private int clampPadding(int desiredPadding) {
2890 return Math.max(desiredPadding, mIntrinsicPadding);
2891 }
2892
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002893 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfed1ab62014-06-17 14:10:33 -07002894 private float getRubberBandFactor(boolean onTop) {
2895 if (!onTop) {
2896 return RUBBER_BAND_FACTOR_NORMAL;
2897 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002898 if (mExpandedInThisMotion) {
2899 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002900 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002901 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2902 } else if (mScrolledToTopOnFirstDown) {
2903 return 1.0f;
2904 }
2905 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002906 }
2907
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002908 /**
2909 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2910 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2911 * overscroll view (e.g. expand QS).
2912 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002913 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002914 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002915 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002916 || !mScrolledToTopOnFirstDown;
2917 }
2918
Selim Cinek67b22602014-03-10 15:40:16 +01002919
Selim Cinek67b22602014-03-10 15:40:16 +01002920
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002921 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef5127e2015-12-21 16:55:58 -08002922 public void setChildTransferInProgress(boolean childTransferInProgress) {
Ned Burnsd4a69f72019-06-19 19:49:19 -04002923 Assert.isMainThread();
Selim Cinekef5127e2015-12-21 16:55:58 -08002924 mChildTransferInProgress = childTransferInProgress;
2925 }
2926
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002927 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Christoph Studer068f5922014-04-08 17:43:07 -04002928 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002929 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002930 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002931 // we only call our internal methods if this is actually a removal and not just a
2932 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002933 if (!mChildTransferInProgress) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002934 onViewRemovedInternal((ExpandableView) child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002935 }
2936 }
2937
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002938 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002939 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -05002940 public void cleanUpViewStateForEntry(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04002941 View child = entry.getRow();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04002942 if (child == mSwipeHelper.getTranslatingParentView()) {
2943 mSwipeHelper.clearTranslatingParentView();
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002944 }
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002945 }
2946
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002947 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002948 private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002949 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002950 // This is only a position change, don't do anything special
2951 return;
2952 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002953 child.setOnHeightChangedListener(null);
2954 updateScrollStateForRemovedChild(child);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002955 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002956 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002957 if (!mSwipedOutViews.contains(child)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002958 || Math.abs(child.getTranslation()) != child.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002959 container.addTransientView(child, 0);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002960 child.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002961 }
2962 } else {
2963 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002964 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002965 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002966
Selim Cineke9bad242016-06-15 11:46:37 -07002967 focusNextViewIfFocused(child);
2968 }
2969
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002970 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke9bad242016-06-15 11:46:37 -07002971 private void focusNextViewIfFocused(View view) {
2972 if (view instanceof ExpandableNotificationRow) {
2973 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2974 if (row.shouldRefocusOnDismiss()) {
2975 View nextView = row.getChildAfterViewWhenDismissed();
2976 if (nextView == null) {
2977 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2978 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2979 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002980 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002981 }
2982 if (nextView != null) {
2983 nextView.requestAccessibilityFocus();
2984 }
2985 }
2986 }
2987
Selim Cinekc27437b2014-05-14 10:23:33 +02002988 }
2989
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002990 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01002991 private boolean isChildInGroup(View child) {
2992 return child instanceof ExpandableNotificationRow
2993 && mGroupManager.isChildInGroupWithSummary(
Ned Burns1c2b85a42019-11-14 15:37:03 -05002994 ((ExpandableNotificationRow) child).getEntry().getSbn());
Selim Cinekb5605e52015-02-20 18:21:41 +01002995 }
2996
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002997 /**
2998 * Generate a remove animation for a child view.
2999 *
3000 * @param child The view to generate the remove animation for.
3001 * @return Whether an animation was generated.
3002 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003003 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003004 private boolean generateRemoveAnimation(ExpandableView child) {
Selim Cineke0890e52015-06-17 11:17:08 -07003005 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07003006 mAddedHeadsUpChildren.remove(child);
3007 return false;
3008 }
Selim Cinek0fccc722015-07-29 17:04:36 -07003009 if (isClickedHeadsUp(child)) {
Selim Cinek9dd0d042018-05-14 18:12:42 -07003010 // An animation is already running, add it transiently
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003011 mClearTransientViewsWhenFinished.add(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07003012 return true;
3013 }
Selim Cinekb5605e52015-02-20 18:21:41 +01003014 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07003015 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02003016 // Generate Animations
3017 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003018 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003019 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02003020 } else {
3021 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003022 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003023 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02003024 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003025 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003026 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003027 }
3028
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003029 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek0fccc722015-07-29 17:04:36 -07003030 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09003031 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07003032 }
3033
Selim Cineke0890e52015-06-17 11:17:08 -07003034 /**
3035 * Remove a removed child view from the heads up animations if it was just added there
3036 *
3037 * @return whether any child was removed from the list to animate
3038 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003039 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke0890e52015-06-17 11:17:08 -07003040 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
3041 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003042 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
3043 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07003044 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003045 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07003046 mTmpList.add(eventPair);
3047 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003048 }
3049 }
Selim Cineke0890e52015-06-17 11:17:08 -07003050 if (hasAddEvent) {
3051 // This child was just added lets remove all events.
3052 mHeadsUpChangeAnimations.removeAll(mTmpList);
Jason Monke59dc402018-08-16 12:05:01 -04003053 ((ExpandableNotificationRow) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07003054 }
3055 mTmpList.clear();
3056 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003057 }
3058
Selim Cinek572bbd42014-04-25 16:43:27 +02003059 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01003060 * @param child the child to query
3061 * @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 -04003062 * not expanded
Selim Cinekb5605e52015-02-20 18:21:41 +01003063 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003064 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003065 private boolean isChildInInvisibleGroup(View child) {
3066 if (child instanceof ExpandableNotificationRow) {
3067 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Ned Burnsf81c4c42019-01-07 14:10:43 -05003068 NotificationEntry groupSummary =
Ned Burns1c2b85a42019-11-14 15:37:03 -05003069 mGroupManager.getGroupSummary(row.getEntry().getSbn());
Evan Laird94492852018-10-25 13:43:01 -04003070 if (groupSummary != null && groupSummary.getRow() != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07003071 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01003072 }
3073 }
3074 return false;
3075 }
3076
3077 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02003078 * Updates the scroll position when a child was removed
3079 *
3080 * @param removedChild the removed child
3081 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003082 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek61633a82016-01-25 15:54:10 -08003083 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003084 if (ANCHOR_SCROLLING) {
3085 if (removedChild == mScrollAnchorView) {
3086 ExpandableView firstChild = getFirstChildNotGone();
3087 if (firstChild != null) {
3088 mScrollAnchorView = firstChild;
3089 } else {
3090 mScrollAnchorView = mShelf;
3091 }
3092 // Adjust anchor view Y by the distance between the old and new anchors
3093 // so that there's no visible change.
3094 mScrollAnchorViewY +=
3095 mScrollAnchorView.getTranslationY() - removedChild.getTranslationY();
3096 }
3097 updateScrollAnchor();
3098 // TODO: once we're recycling this will need to check the adapter position of the child
3099 if (mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY > 0) {
3100 mScrollAnchorViewY = 0;
3101 }
3102 updateOnScrollChange();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003103 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003104 int startingPosition = getPositionInLinearLayout(removedChild);
3105 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
3106 int padding;
3107 if (increasedPaddingAmount >= 0) {
3108 padding = (int) NotificationUtils.interpolate(
3109 mPaddingBetweenElements,
3110 mIncreasedPaddingBetweenElements,
3111 increasedPaddingAmount);
3112 } else {
3113 padding = (int) NotificationUtils.interpolate(
3114 0,
3115 mPaddingBetweenElements,
3116 1.0f + increasedPaddingAmount);
3117 }
3118 int childHeight = getIntrinsicHeight(removedChild) + padding;
3119 int endPosition = startingPosition + childHeight;
3120 if (endPosition <= mOwnScrollY) {
3121 // This child is fully scrolled of the top, so we have to deduct its height from the
3122 // scrollPosition
3123 setOwnScrollY(mOwnScrollY - childHeight);
3124 } else if (startingPosition < mOwnScrollY) {
3125 // This child is currently being scrolled into, set the scroll position to the
3126 // start of this child
3127 setOwnScrollY(startingPosition);
3128 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003129 }
3130 }
3131
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003132 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd7c4e002014-07-04 18:36:42 +02003133 private int getIntrinsicHeight(View view) {
3134 if (view instanceof ExpandableView) {
3135 ExpandableView expandableView = (ExpandableView) view;
3136 return expandableView.getIntrinsicHeight();
3137 }
3138 return view.getHeight();
3139 }
3140
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003141 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1f624952017-06-08 19:11:50 -07003142 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07003143 ExpandableNotificationRow childInGroup = null;
3144 ExpandableNotificationRow requestedRow = null;
3145 if (isChildInGroup(requestedView)) {
3146 // We're asking for a child in a group. Calculate the position of the parent first,
3147 // then within the parent.
3148 childInGroup = (ExpandableNotificationRow) requestedView;
3149 requestedView = requestedRow = childInGroup.getNotificationParent();
3150 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003151 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08003152 float previousPaddingRequest = mPaddingBetweenElements;
3153 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02003154 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08003155 ExpandableView child = (ExpandableView) getChildAt(i);
3156 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07003157 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08003158 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003159 float padding;
3160 if (increasedPaddingAmount >= 0.0f) {
3161 padding = (int) NotificationUtils.interpolate(
3162 previousPaddingRequest,
3163 mIncreasedPaddingBetweenElements,
3164 increasedPaddingAmount);
3165 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08003166 mPaddingBetweenElements,
3167 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08003168 increasedPaddingAmount);
3169 } else {
3170 int ownPadding = (int) NotificationUtils.interpolate(
3171 0,
3172 mPaddingBetweenElements,
3173 1.0f + increasedPaddingAmount);
3174 if (previousPaddingAmount > 0.0f) {
3175 padding = (int) NotificationUtils.interpolate(
3176 ownPadding,
3177 mIncreasedPaddingBetweenElements,
3178 previousPaddingAmount);
3179 } else {
3180 padding = ownPadding;
3181 }
3182 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08003183 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08003184 if (position != 0) {
3185 position += padding;
3186 }
3187 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08003188 }
Adrian Roos4a579672016-05-24 16:54:37 -07003189 if (child == requestedView) {
3190 if (requestedRow != null) {
3191 position += requestedRow.getPositionOfChild(childInGroup);
3192 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003193 return position;
3194 }
Selim Cinek61633a82016-01-25 15:54:10 -08003195 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02003196 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003197 }
3198 }
3199 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02003200 }
3201
3202 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003203 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adam Powell6690d012015-06-17 16:41:56 -07003204 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02003205 super.onViewAdded(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003206 onViewAddedInternal((ExpandableView) child);
Selim Cinekb5605e52015-02-20 18:21:41 +01003207 }
3208
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003209 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08003210 private void updateFirstAndLastBackgroundViews() {
Gus Prevase2d6f042018-10-17 15:25:30 -04003211 NotificationSection firstSection = getFirstVisibleSection();
3212 NotificationSection lastSection = getLastVisibleSection();
Gus Prevasda13cfa2018-11-20 14:58:48 -05003213 ActivatableNotificationView previousFirstChild =
3214 firstSection == null ? null : firstSection.getFirstVisibleChild();
3215 ActivatableNotificationView previousLastChild =
3216 lastSection == null ? null : lastSection.getLastVisibleChild();
Gus Prevase2d6f042018-10-17 15:25:30 -04003217
Selim Cinek614576e2016-01-20 10:54:09 -08003218 ActivatableNotificationView firstChild = getFirstChildWithBackground();
3219 ActivatableNotificationView lastChild = getLastChildWithBackground();
Evan Laird25f02752019-08-14 19:25:06 -04003220 boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsForAllSections(
3221 mSections, getChildrenWithBackground());
Gus Prevase2d6f042018-10-17 15:25:30 -04003222
Selim Cinek614576e2016-01-20 10:54:09 -08003223 if (mAnimationsEnabled && mIsExpanded) {
Gus Prevasda13cfa2018-11-20 14:58:48 -05003224 mAnimateNextBackgroundTop = firstChild != previousFirstChild;
Selim Cinek6f0a62a2019-04-09 18:40:12 -07003225 mAnimateNextBackgroundBottom = lastChild != previousLastChild || mAnimateBottomOnLayout;
Gus Prevase2d6f042018-10-17 15:25:30 -04003226 mAnimateNextSectionBoundsChange = sectionViewsChanged;
Selim Cinek614576e2016-01-20 10:54:09 -08003227 } else {
3228 mAnimateNextBackgroundTop = false;
3229 mAnimateNextBackgroundBottom = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04003230 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08003231 }
Selim Cinekdb167372016-11-17 15:41:17 -08003232 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Gus Prevase2d6f042018-10-17 15:25:30 -04003233 mRoundnessManager.updateRoundedChildren(mSections);
Selim Cinek6f0a62a2019-04-09 18:40:12 -07003234 mAnimateBottomOnLayout = false;
Selim Cinek515b2032017-11-15 10:20:19 -08003235 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003236 }
3237
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003238 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003239 private void onViewAddedInternal(ExpandableView child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003240 updateHideSensitiveForChild(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003241 child.setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003242 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003243 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003244 updateChronometerForChild(child);
Gus Prevasa18dc572019-01-14 16:11:22 -05003245 if (child instanceof ExpandableNotificationRow) {
3246 ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
3247 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05003248 if (ANCHOR_SCROLLING) {
3249 // TODO: once we're recycling this will need to check the adapter position of the child
3250 if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) {
3251 // New child was added at the top while we're scrolled to the top;
3252 // make it the new anchor view so that we stay at the top.
3253 mScrollAnchorView = child;
3254 }
3255 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003256 }
3257
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003258 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003259 private void updateHideSensitiveForChild(ExpandableView child) {
3260 child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003261 }
3262
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003263 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003264 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003265 public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
Selim Cinekd1395642016-04-28 12:22:42 -07003266 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003267 }
3268
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003269 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003270 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003271 public void notifyGroupChildAdded(ExpandableView row) {
Selim Cinekb5605e52015-02-20 18:21:41 +01003272 onViewAddedInternal(row);
3273 }
3274
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003275 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003276 public void setAnimationsEnabled(boolean animationsEnabled) {
3277 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003278 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003279 if (!animationsEnabled) {
3280 mSwipedOutViews.clear();
3281 mChildrenToRemoveAnimated.clear();
3282 clearTemporaryViewsInGroup(this);
3283 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003284 }
3285
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003286 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003287 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003288 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003289 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003290 int childCount = getChildCount();
3291 for (int i = 0; i < childCount; i++) {
3292 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003293 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003294 updateAnimationState(running, child);
3295 }
3296 }
3297
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003298 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek51ae05d2014-09-09 15:51:38 +02003299 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003300 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003301 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003302 }
3303
Selim Cinek2627d722018-01-19 12:16:49 -08003304 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003305 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek2627d722018-01-19 12:16:49 -08003306 public void setExpandingNotification(ExpandableNotificationRow row) {
3307 mAmbientState.setExpandingNotification(row);
3308 requestChildrenUpdate();
3309 }
3310
3311 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003312 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek8875de12018-03-22 10:14:32 -07003313 public void bindRow(ExpandableNotificationRow row) {
Selim Cinekf0c79e12018-05-14 17:17:31 -07003314 row.setHeadsUpAnimatingAwayListener(animatingAway -> {
3315 mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
3316 mHeadsUpAppearanceController.updateHeader(row.getEntry());
3317 });
Selim Cinek8875de12018-03-22 10:14:32 -07003318 }
3319
3320 @Override
Selim Cinekfdf80332019-03-07 17:29:55 -08003321 public boolean containsView(View v) {
3322 return v.getParent() == this;
3323 }
3324
3325 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003326 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek2627d722018-01-19 12:16:49 -08003327 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3328 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3329 requestChildrenUpdate();
3330 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003331
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003332 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003333 private void updateAnimationState(boolean running, View child) {
3334 if (child instanceof ExpandableNotificationRow) {
3335 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3336 row.setIconAnimationRunning(running);
3337 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003338 }
3339
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003340 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003341 public boolean isAddOrRemoveAnimationPending() {
3342 return mNeedsAnimation
3343 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3344 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003345
3346 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003347 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003348 public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
Selim Cinek820ba2d2019-06-18 18:59:09 -07003349 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress && !isFullyHidden()) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003350 // Generate Animations
3351 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003352 if (fromMoreCard) {
3353 mFromMoreCardAdditions.add(child);
3354 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003355 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003356 }
Selim Cinek820ba2d2019-06-18 18:59:09 -07003357 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress
3358 && !isFullyHidden()) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003359 mAddedHeadsUpChildren.add(child);
3360 mChildrenToAddAnimated.remove(child);
3361 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003362 }
3363
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003364 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003365 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003366 public void changeViewPosition(ExpandableView child, int newIndex) {
Ned Burnsd4a69f72019-06-19 19:49:19 -04003367 Assert.isMainThread();
3368 if (mChangePositionInProgress) {
3369 throw new IllegalStateException("Reentrant call to changeViewPosition");
3370 }
3371
Dan Sandlereceda3d2014-07-21 15:35:01 -04003372 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003373
3374 if (currentIndex == -1) {
3375 boolean isTransient = false;
3376 if (child instanceof ExpandableNotificationRow
Jason Monke59dc402018-08-16 12:05:01 -04003377 && ((ExpandableNotificationRow) child).getTransientContainer() != null) {
Rohan Shah8ee53652018-04-05 11:13:50 -07003378 isTransient = true;
3379 }
3380 Log.e(TAG, "Attempting to re-position "
3381 + (isTransient ? "transient" : "")
3382 + " view {"
3383 + child
3384 + "}");
3385 return;
3386 }
3387
Dan Sandlereceda3d2014-07-21 15:35:01 -04003388 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003389 mChangePositionInProgress = true;
Jason Monke59dc402018-08-16 12:05:01 -04003390 ((ExpandableView) child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003391 removeView(child);
3392 addView(child, newIndex);
Jason Monke59dc402018-08-16 12:05:01 -04003393 ((ExpandableView) child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003394 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003395 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003396 mChildrenChangingPositions.add(child);
3397 mNeedsAnimation = true;
3398 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003399 }
3400 }
3401
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003402 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf4c19962014-05-01 21:55:31 +02003403 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003404 if (mNeedsAnimation) {
Rohan Shahb9d500a2018-06-25 16:27:16 -07003405 generateAllAnimationEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003406 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003407 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003408 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003409 setAnimationRunning(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003410 mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003411 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003412 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003413 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003414 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003415 } else {
3416 applyCurrentState();
3417 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003418 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003419 }
3420
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003421 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shahb9d500a2018-06-25 16:27:16 -07003422 private void generateAllAnimationEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003423 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003424 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003425 generateChildAdditionEvents();
3426 generatePositionChangeEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003427 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003428 generateActivateEvent();
3429 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003430 generateHideSensitiveEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003431 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003432 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003433 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003434 generateAnimateEverythingEvent();
Selim Cinek572bbd42014-04-25 16:43:27 +02003435 }
3436
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003437 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003438 private void generateHeadsUpAnimationEvents() {
3439 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003440 ExpandableNotificationRow row = eventPair.first;
3441 boolean isHeadsUp = eventPair.second;
Selim Cineke3c6e462019-06-24 19:37:06 -07003442 if (isHeadsUp != row.isHeadsUp()) {
3443 // For cases where we have a heads up showing and appearing again we shouldn't
3444 // do the animations at all.
3445 continue;
3446 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003447 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3448 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003449 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cineke3c6e462019-06-24 19:37:06 -07003450 boolean performDisappearAnimation = !mIsExpanded
3451 // Only animate if we still have pinned heads up, otherwise we just have the
3452 // regular collapse animation of the lock screen
3453 || (mKeyguardBypassController.getBypassEnabled() && onKeyguard()
3454 && mHeadsUpManager.hasPinnedHeadsUp());
3455 if (performDisappearAnimation && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003456 type = row.wasJustClicked()
3457 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3458 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003459 if (row.isChildInGroup()) {
3460 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003461 row.setHeadsUpAnimatingAway(false);
Selim Cinekf93bf3e2018-05-08 14:43:21 -07003462 continue;
Selim Cinek76e813c2016-07-14 11:16:58 -07003463 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003464 } else {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003465 ExpandableViewState viewState = row.getViewState();
Selim Cinekeaee9c02015-06-25 11:04:20 -04003466 if (viewState == null) {
3467 // A view state was never generated for this view, so we don't need to animate
3468 // this. This may happen with notification children.
3469 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003470 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003471 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3472 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3473 // Our custom add animation
3474 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3475 } else {
3476 // Normal add animation
3477 type = AnimationEvent.ANIMATION_TYPE_ADD;
3478 }
3479 onBottom = !pinnedAndClosed;
3480 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003481 }
3482 AnimationEvent event = new AnimationEvent(row, type);
3483 event.headsUpFromBottom = onBottom;
3484 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003485 }
3486 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003487 mAddedHeadsUpChildren.clear();
3488 }
3489
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003490 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbbcebde2016-11-09 18:28:20 -08003491 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003492 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3493 return false;
3494 }
3495 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003496 }
3497
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003498 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003499 private void generateGroupExpansionEvent() {
3500 // Generate a group expansion/collapsing event if there is such a group at all
3501 if (mExpandedGroupView != null) {
3502 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3503 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3504 mExpandedGroupView = null;
3505 }
3506 }
3507
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003508 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineka5e211b2014-08-11 17:35:48 +02003509 private void generateViewResizeEvent() {
3510 if (mNeedViewResizeAnimation) {
felkachangd7835b02018-07-17 18:18:13 +08003511 boolean hasDisappearAnimation = false;
3512 for (AnimationEvent animationEvent : mAnimationEvents) {
3513 final int type = animationEvent.animationType;
3514 if (type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
Jason Monke59dc402018-08-16 12:05:01 -04003515 || type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
felkachangd7835b02018-07-17 18:18:13 +08003516 hasDisappearAnimation = true;
3517 break;
3518 }
3519 }
3520
3521 if (!hasDisappearAnimation) {
3522 mAnimationEvents.add(
3523 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3524 }
Selim Cineka5e211b2014-08-11 17:35:48 +02003525 }
3526 mNeedViewResizeAnimation = false;
3527 }
3528
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003529 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003530 private void generateChildRemovalEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003531 for (ExpandableView child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003532 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003533
3534 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003535 float removedTranslation = child.getTranslationY();
3536 boolean ignoreChildren = true;
3537 if (child instanceof ExpandableNotificationRow) {
3538 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3539 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3540 removedTranslation = row.getTranslationWhenRemoved();
3541 ignoreChildren = false;
3542 }
Selim Cinek51052042017-07-04 12:07:55 +02003543 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003544 }
Selim Cinek51052042017-07-04 12:07:55 +02003545 if (!childWasSwipedOut) {
3546 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003547 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Rohan Shaha7594962018-05-22 10:59:30 -07003548
3549 if (childWasSwipedOut && child instanceof ExpandableView) {
3550 // Clean up any potential transient views if the child has already been swiped
3551 // out, as we won't be animating it further (due to its height already being
3552 // clipped to 0.
3553 ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
3554 if (transientContainer != null) {
3555 transientContainer.removeTransientView(child);
3556 }
3557 }
Selim Cinek51052042017-07-04 12:07:55 +02003558 }
3559 int animationType = childWasSwipedOut
3560 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3561 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3562 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003563 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3564 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003565 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003566 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003567 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003568 mChildrenToRemoveAnimated.clear();
3569 }
3570
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003571 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003572 private void generatePositionChangeEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003573 for (ExpandableView child : mChildrenChangingPositions) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003574 mAnimationEvents.add(new AnimationEvent(child,
3575 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3576 }
3577 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003578 if (mGenerateChildOrderChangedEvent) {
3579 mAnimationEvents.add(new AnimationEvent(null,
3580 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3581 mGenerateChildOrderChangedEvent = false;
3582 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003583 }
3584
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003585 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003586 private void generateChildAdditionEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003587 for (ExpandableView child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003588 if (mFromMoreCardAdditions.contains(child)) {
3589 mAnimationEvents.add(new AnimationEvent(child,
3590 AnimationEvent.ANIMATION_TYPE_ADD,
3591 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3592 } else {
3593 mAnimationEvents.add(new AnimationEvent(child,
3594 AnimationEvent.ANIMATION_TYPE_ADD));
3595 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003596 }
3597 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003598 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003599 }
3600
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003601 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003602 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003603 if (mTopPaddingNeedsAnimation) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003604 AnimationEvent event;
Selim Cinekc1d9ab22019-05-21 18:08:30 -07003605 if (mAmbientState.isDozing()) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003606 event = new AnimationEvent(null /* view */,
3607 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
3608 KeyguardSliceView.DEFAULT_ANIM_DURATION);
3609 } else {
3610 event = new AnimationEvent(null /* view */,
3611 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
3612 }
3613 mAnimationEvents.add(event);
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003614 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003615 mTopPaddingNeedsAnimation = false;
3616 }
3617
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003618 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003619 private void generateActivateEvent() {
3620 if (mActivateNeedsAnimation) {
3621 mAnimationEvents.add(
3622 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3623 }
3624 mActivateNeedsAnimation = false;
3625 }
3626
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003627 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd9acca52014-09-01 22:33:25 +02003628 private void generateAnimateEverythingEvent() {
3629 if (mEverythingNeedsAnimation) {
3630 mAnimationEvents.add(
3631 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3632 }
3633 mEverythingNeedsAnimation = false;
3634 }
3635
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003636 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003637 private void generateDimmedEvent() {
3638 if (mDimmedNeedsAnimation) {
3639 mAnimationEvents.add(
3640 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3641 }
3642 mDimmedNeedsAnimation = false;
3643 }
3644
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003645 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggiae441282014-08-01 02:45:18 +02003646 private void generateHideSensitiveEvent() {
3647 if (mHideSensitiveNeedsAnimation) {
3648 mAnimationEvents.add(
3649 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3650 }
3651 mHideSensitiveNeedsAnimation = false;
3652 }
3653
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003654 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003655 private void generateGoToFullShadeEvent() {
3656 if (mGoToFullShadeNeedsAnimation) {
3657 mAnimationEvents.add(
3658 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3659 }
3660 mGoToFullShadeNeedsAnimation = false;
3661 }
3662
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003663 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
3664 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003665 return new StackScrollAlgorithm(context, this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003666 }
3667
3668 /**
3669 * @return Whether a y coordinate is inside the content.
3670 */
3671 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
3672 public boolean isInContentBounds(float y) {
3673 return y < getHeight() - getEmptyBottomMargin();
3674 }
3675
3676 @ShadeViewRefactor(RefactorComponent.INPUT)
3677 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
3678 mLongPressListener = listener;
3679 }
3680
3681 @Override
3682 @ShadeViewRefactor(RefactorComponent.INPUT)
3683 public boolean onTouchEvent(MotionEvent ev) {
3684 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
3685 || ev.getActionMasked() == MotionEvent.ACTION_UP;
3686 handleEmptySpaceClick(ev);
3687 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003688 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003689 if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
3690 if (isCancelOrUp) {
3691 mExpandHelper.onlyObserveMovements(false);
3692 }
3693 boolean wasExpandingBefore = mExpandingNotification;
3694 expandWantsIt = mExpandHelper.onTouchEvent(ev);
3695 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
3696 && !mDisallowScrollingInThisMotion) {
3697 dispatchDownEventToScroller(ev);
3698 }
3699 }
3700 boolean scrollerWantsIt = false;
3701 if (mIsExpanded && !swipingInProgress && !mExpandingNotification
3702 && !mDisallowScrollingInThisMotion) {
3703 scrollerWantsIt = onScrollTouch(ev);
3704 }
3705 boolean horizontalSwipeWantsIt = false;
3706 if (!mIsBeingDragged
3707 && !mExpandingNotification
3708 && !mExpandedInThisMotion
3709 && !mOnlyScrollingInThisMotion
3710 && !mDisallowDismissInThisMotion) {
3711 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
3712 }
3713
3714 // Check if we need to clear any snooze leavebehinds
3715 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
3716 if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
3717 && guts.getGutsContent() instanceof NotificationSnooze) {
3718 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
3719 if ((ns.isExpanded() && isCancelOrUp)
3720 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
3721 // If the leavebehind is expanded we clear it on the next up event, otherwise we
3722 // clear it on the next non-horizontal swipe or expand event.
3723 checkSnoozeLeavebehind();
3724 }
3725 }
3726 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
3727 mCheckForLeavebehind = true;
3728 }
3729 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
3730 }
3731
3732 @ShadeViewRefactor(RefactorComponent.INPUT)
3733 private void dispatchDownEventToScroller(MotionEvent ev) {
3734 MotionEvent downEvent = MotionEvent.obtain(ev);
3735 downEvent.setAction(MotionEvent.ACTION_DOWN);
3736 onScrollTouch(downEvent);
3737 downEvent.recycle();
3738 }
3739
3740 @Override
3741 @ShadeViewRefactor(RefactorComponent.INPUT)
3742 public boolean onGenericMotionEvent(MotionEvent event) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003743 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003744 || mDisallowScrollingInThisMotion) {
3745 return false;
3746 }
3747 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
3748 switch (event.getAction()) {
3749 case MotionEvent.ACTION_SCROLL: {
3750 if (!mIsBeingDragged) {
3751 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3752 if (vscroll != 0) {
3753 final int delta = (int) (vscroll * getVerticalScrollFactor());
Gus Prevas0fa58d62019-01-11 13:58:40 -05003754 if (ANCHOR_SCROLLING) {
3755 mScrollAnchorViewY -= delta;
3756 updateScrollAnchor();
3757 clampScrollPosition();
3758 updateOnScrollChange();
3759 } else {
3760 final int range = getScrollRange();
3761 int oldScrollY = mOwnScrollY;
3762 int newScrollY = oldScrollY - delta;
3763 if (newScrollY < 0) {
3764 newScrollY = 0;
3765 } else if (newScrollY > range) {
3766 newScrollY = range;
3767 }
3768 if (newScrollY != oldScrollY) {
3769 setOwnScrollY(newScrollY);
3770 return true;
3771 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003772 }
3773 }
3774 }
3775 }
3776 }
3777 }
3778 return super.onGenericMotionEvent(event);
3779 }
3780
3781 @ShadeViewRefactor(RefactorComponent.INPUT)
3782 private boolean onScrollTouch(MotionEvent ev) {
3783 if (!isScrollingEnabled()) {
3784 return false;
3785 }
3786 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
3787 return false;
3788 }
3789 mForcedScroll = null;
3790 initVelocityTrackerIfNotExists();
3791 mVelocityTracker.addMovement(ev);
3792
3793 final int action = ev.getAction();
3794
3795 switch (action & MotionEvent.ACTION_MASK) {
3796 case MotionEvent.ACTION_DOWN: {
3797 if (getChildCount() == 0 || !isInContentBounds(ev)) {
3798 return false;
3799 }
3800 boolean isBeingDragged = !mScroller.isFinished();
3801 setIsBeingDragged(isBeingDragged);
3802 /*
3803 * If being flinged and user touches, stop the fling. isFinished
3804 * will be false if being flinged.
3805 */
3806 if (!mScroller.isFinished()) {
3807 mScroller.forceFinished(true);
3808 }
3809
3810 // Remember where the motion event started
3811 mLastMotionY = (int) ev.getY();
3812 mDownX = (int) ev.getX();
3813 mActivePointerId = ev.getPointerId(0);
3814 break;
3815 }
3816 case MotionEvent.ACTION_MOVE:
3817 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
3818 if (activePointerIndex == -1) {
3819 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
3820 break;
3821 }
3822
3823 final int y = (int) ev.getY(activePointerIndex);
3824 final int x = (int) ev.getX(activePointerIndex);
3825 int deltaY = mLastMotionY - y;
3826 final int xDiff = Math.abs(x - mDownX);
3827 final int yDiff = Math.abs(deltaY);
3828 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
3829 setIsBeingDragged(true);
3830 if (deltaY > 0) {
3831 deltaY -= mTouchSlop;
3832 } else {
3833 deltaY += mTouchSlop;
3834 }
3835 }
3836 if (mIsBeingDragged) {
3837 // Scroll to follow the motion event
3838 mLastMotionY = y;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003839 float scrollAmount;
Gus Prevas0fa58d62019-01-11 13:58:40 -05003840 int range;
3841 if (ANCHOR_SCROLLING) {
3842 range = 0; // unused in the methods it's being passed to
3843 } else {
3844 range = getScrollRange();
3845 if (mExpandedInThisMotion) {
3846 range = Math.min(range, mMaxScrollAfterExpand);
3847 }
3848 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003849 if (deltaY < 0) {
3850 scrollAmount = overScrollDown(deltaY);
3851 } else {
3852 scrollAmount = overScrollUp(deltaY, range);
3853 }
3854
3855 // Calling customOverScrollBy will call onCustomOverScrolled, which
3856 // sets the scrolling if applicable.
3857 if (scrollAmount != 0.0f) {
3858 // The scrolling motion could not be compensated with the
3859 // existing overScroll, we have to scroll the view
3860 customOverScrollBy((int) scrollAmount, mOwnScrollY,
3861 range, getHeight() / 2);
3862 // If we're scrolling, leavebehinds should be dismissed
3863 checkSnoozeLeavebehind();
3864 }
3865 }
3866 break;
3867 case MotionEvent.ACTION_UP:
3868 if (mIsBeingDragged) {
3869 final VelocityTracker velocityTracker = mVelocityTracker;
3870 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
3871 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
3872
3873 if (shouldOverScrollFling(initialVelocity)) {
3874 onOverScrollFling(true, initialVelocity);
3875 } else {
3876 if (getChildCount() > 0) {
3877 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
3878 float currentOverScrollTop = getCurrentOverScrollAmount(true);
3879 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
3880 fling(-initialVelocity);
3881 } else {
3882 onOverScrollFling(false, initialVelocity);
3883 }
3884 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003885 if (ANCHOR_SCROLLING) {
3886 // TODO
3887 } else {
3888 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3889 getScrollRange())) {
3890 animateScroll();
3891 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003892 }
3893 }
3894 }
3895 }
3896 mActivePointerId = INVALID_POINTER;
3897 endDrag();
3898 }
3899
3900 break;
3901 case MotionEvent.ACTION_CANCEL:
3902 if (mIsBeingDragged && getChildCount() > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003903 if (ANCHOR_SCROLLING) {
3904 // TODO
3905 } else {
3906 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3907 getScrollRange())) {
3908 animateScroll();
3909 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003910 }
3911 mActivePointerId = INVALID_POINTER;
3912 endDrag();
3913 }
3914 break;
3915 case MotionEvent.ACTION_POINTER_DOWN: {
3916 final int index = ev.getActionIndex();
3917 mLastMotionY = (int) ev.getY(index);
3918 mDownX = (int) ev.getX(index);
3919 mActivePointerId = ev.getPointerId(index);
3920 break;
3921 }
3922 case MotionEvent.ACTION_POINTER_UP:
3923 onSecondaryPointerUp(ev);
3924 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
3925 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
3926 break;
3927 }
3928 return true;
3929 }
3930
3931 @ShadeViewRefactor(RefactorComponent.INPUT)
3932 protected boolean isInsideQsContainer(MotionEvent ev) {
3933 return ev.getY() < mQsContainer.getBottom();
3934 }
3935
3936 @ShadeViewRefactor(RefactorComponent.INPUT)
3937 private void onOverScrollFling(boolean open, int initialVelocity) {
3938 if (mOverscrollTopChangedListener != null) {
3939 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
3940 }
3941 mDontReportNextOverScroll = true;
3942 setOverScrollAmount(0.0f, true, false);
3943 }
3944
3945
3946 @ShadeViewRefactor(RefactorComponent.INPUT)
3947 private void onSecondaryPointerUp(MotionEvent ev) {
3948 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
3949 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
3950 final int pointerId = ev.getPointerId(pointerIndex);
3951 if (pointerId == mActivePointerId) {
3952 // This was our active pointer going up. Choose a new
3953 // active pointer and adjust accordingly.
3954 // TODO: Make this decision more intelligent.
3955 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
3956 mLastMotionY = (int) ev.getY(newPointerIndex);
3957 mActivePointerId = ev.getPointerId(newPointerIndex);
3958 if (mVelocityTracker != null) {
3959 mVelocityTracker.clear();
3960 }
3961 }
3962 }
3963
3964 @ShadeViewRefactor(RefactorComponent.INPUT)
3965 private void endDrag() {
3966 setIsBeingDragged(false);
3967
3968 recycleVelocityTracker();
3969
3970 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
3971 setOverScrollAmount(0, true /* onTop */, true /* animate */);
3972 }
3973 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
3974 setOverScrollAmount(0, false /* onTop */, true /* animate */);
3975 }
3976 }
3977
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003978 @Override
3979 @ShadeViewRefactor(RefactorComponent.INPUT)
3980 public boolean onInterceptTouchEvent(MotionEvent ev) {
3981 initDownStates(ev);
3982 handleEmptySpaceClick(ev);
3983 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003984 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003985 if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
3986 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
3987 }
3988 boolean scrollWantsIt = false;
3989 if (!swipingInProgress && !mExpandingNotification) {
3990 scrollWantsIt = onInterceptTouchEventScroll(ev);
3991 }
3992 boolean swipeWantsIt = false;
3993 if (!mIsBeingDragged
3994 && !mExpandingNotification
3995 && !mExpandedInThisMotion
3996 && !mOnlyScrollingInThisMotion
3997 && !mDisallowDismissInThisMotion) {
3998 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
3999 }
4000 // Check if we need to clear any snooze leavebehinds
4001 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
4002 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
4003 if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
4004 !expandWantsIt && !scrollWantsIt) {
4005 mCheckForLeavebehind = false;
4006 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
4007 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4008 false /* resetMenu */);
4009 }
4010 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
4011 mCheckForLeavebehind = true;
4012 }
4013 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
4014 }
4015
4016 @ShadeViewRefactor(RefactorComponent.INPUT)
4017 private void handleEmptySpaceClick(MotionEvent ev) {
4018 switch (ev.getActionMasked()) {
4019 case MotionEvent.ACTION_MOVE:
4020 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
4021 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
4022 mTouchIsClick = false;
4023 }
4024 break;
4025 case MotionEvent.ACTION_UP:
4026 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
4027 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
4028 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
4029 }
4030 break;
4031 }
4032 }
4033
4034 @ShadeViewRefactor(RefactorComponent.INPUT)
4035 private void initDownStates(MotionEvent ev) {
4036 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
4037 mExpandedInThisMotion = false;
4038 mOnlyScrollingInThisMotion = !mScroller.isFinished();
4039 mDisallowScrollingInThisMotion = false;
4040 mDisallowDismissInThisMotion = false;
4041 mTouchIsClick = true;
4042 mInitialTouchX = ev.getX();
4043 mInitialTouchY = ev.getY();
4044 }
4045 }
4046
4047 @Override
4048 @ShadeViewRefactor(RefactorComponent.INPUT)
4049 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
4050 super.requestDisallowInterceptTouchEvent(disallowIntercept);
4051 if (disallowIntercept) {
4052 cancelLongPress();
4053 }
4054 }
4055
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004056 @ShadeViewRefactor(RefactorComponent.INPUT)
Selim Cinek67b22602014-03-10 15:40:16 +01004057 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02004058 if (!isScrollingEnabled()) {
4059 return false;
4060 }
Selim Cinek67b22602014-03-10 15:40:16 +01004061 /*
4062 * This method JUST determines whether we want to intercept the motion.
4063 * If we return true, onMotionEvent will be called and we do the actual
4064 * scrolling there.
4065 */
4066
4067 /*
Jason Monke59dc402018-08-16 12:05:01 -04004068 * Shortcut the most recurring case: the user is in the dragging
4069 * state and is moving their finger. We want to intercept this
4070 * motion.
4071 */
Selim Cinek67b22602014-03-10 15:40:16 +01004072 final int action = ev.getAction();
4073 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
4074 return true;
4075 }
4076
Selim Cinek67b22602014-03-10 15:40:16 +01004077 switch (action & MotionEvent.ACTION_MASK) {
4078 case MotionEvent.ACTION_MOVE: {
4079 /*
4080 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04004081 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01004082 */
4083
4084 /*
Jason Monke59dc402018-08-16 12:05:01 -04004085 * Locally do absolute value. mLastMotionY is set to the y value
4086 * of the down event.
4087 */
Selim Cinek67b22602014-03-10 15:40:16 +01004088 final int activePointerId = mActivePointerId;
4089 if (activePointerId == INVALID_POINTER) {
4090 // If we don't have a valid id, the touch down wasn't on content.
4091 break;
4092 }
4093
4094 final int pointerIndex = ev.findPointerIndex(activePointerId);
4095 if (pointerIndex == -1) {
4096 Log.e(TAG, "Invalid pointerId=" + activePointerId
4097 + " in onInterceptTouchEvent");
4098 break;
4099 }
4100
4101 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02004102 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01004103 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02004104 final int xDiff = Math.abs(x - mDownX);
4105 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01004106 setIsBeingDragged(true);
4107 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004108 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01004109 initVelocityTrackerIfNotExists();
4110 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01004111 }
4112 break;
4113 }
4114
4115 case MotionEvent.ACTION_DOWN: {
4116 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05304117 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07004118 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01004119 setIsBeingDragged(false);
4120 recycleVelocityTracker();
4121 break;
4122 }
4123
4124 /*
4125 * Remember location of down touch.
4126 * ACTION_DOWN always refers to pointer index 0.
4127 */
4128 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004129 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01004130 mActivePointerId = ev.getPointerId(0);
4131
4132 initOrResetVelocityTracker();
4133 mVelocityTracker.addMovement(ev);
4134 /*
Jason Monke59dc402018-08-16 12:05:01 -04004135 * If being flinged and user touches the screen, initiate drag;
4136 * otherwise don't. mScroller.isFinished should be false when
4137 * being flinged.
4138 */
Selim Cinek67b22602014-03-10 15:40:16 +01004139 boolean isBeingDragged = !mScroller.isFinished();
4140 setIsBeingDragged(isBeingDragged);
4141 break;
4142 }
4143
4144 case MotionEvent.ACTION_CANCEL:
4145 case MotionEvent.ACTION_UP:
4146 /* Release the drag */
4147 setIsBeingDragged(false);
4148 mActivePointerId = INVALID_POINTER;
4149 recycleVelocityTracker();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004150 if (ANCHOR_SCROLLING) {
4151 // TODO
4152 } else {
4153 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
4154 animateScroll();
4155 }
Selim Cinek67b22602014-03-10 15:40:16 +01004156 }
4157 break;
4158 case MotionEvent.ACTION_POINTER_UP:
4159 onSecondaryPointerUp(ev);
4160 break;
4161 }
4162
4163 /*
Jason Monke59dc402018-08-16 12:05:01 -04004164 * The only time we want to intercept motion events is if we are in the
4165 * drag mode.
4166 */
Selim Cinek67b22602014-03-10 15:40:16 +01004167 return mIsBeingDragged;
4168 }
4169
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004170 /**
4171 * @return Whether the specified motion event is actually happening over the content.
4172 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004173 @ShadeViewRefactor(RefactorComponent.INPUT)
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004174 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01004175 return isInContentBounds(event.getY());
4176 }
4177
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004178
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004179 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004180 @ShadeViewRefactor(RefactorComponent.INPUT)
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004181 void setIsBeingDragged(boolean isDragged) {
Selim Cinek67b22602014-03-10 15:40:16 +01004182 mIsBeingDragged = isDragged;
4183 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004184 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004185 cancelLongPress();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004186 resetExposedMenuView(true /* animate */, true /* force */);
Selim Cinek67b22602014-03-10 15:40:16 +01004187 }
4188 }
4189
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004190 @ShadeViewRefactor(RefactorComponent.INPUT)
4191 public void requestDisallowLongPress() {
4192 cancelLongPress();
4193 }
4194
4195 @ShadeViewRefactor(RefactorComponent.INPUT)
4196 public void requestDisallowDismiss() {
4197 mDisallowDismissInThisMotion = true;
4198 }
4199
4200 @ShadeViewRefactor(RefactorComponent.INPUT)
4201 public void cancelLongPress() {
4202 mSwipeHelper.cancelLongPress();
4203 }
4204
4205 @ShadeViewRefactor(RefactorComponent.INPUT)
4206 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
4207 mOnEmptySpaceClickListener = listener;
4208 }
4209
4210 /** @hide */
4211 @Override
4212 @ShadeViewRefactor(RefactorComponent.INPUT)
4213 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4214 if (super.performAccessibilityActionInternal(action, arguments)) {
4215 return true;
4216 }
4217 if (!isEnabled()) {
4218 return false;
4219 }
4220 int direction = -1;
4221 switch (action) {
4222 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4223 // fall through
4224 case android.R.id.accessibilityActionScrollDown:
4225 direction = 1;
4226 // fall through
4227 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4228 // fall through
4229 case android.R.id.accessibilityActionScrollUp:
Gus Prevas0fa58d62019-01-11 13:58:40 -05004230 if (ANCHOR_SCROLLING) {
4231 // TODO
4232 } else {
4233 final int viewportHeight =
4234 getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
4235 - mShelf.getIntrinsicHeight();
4236 final int targetScrollY = Math.max(0,
4237 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4238 if (targetScrollY != mOwnScrollY) {
4239 mScroller.startScroll(mScrollX, mOwnScrollY, 0,
4240 targetScrollY - mOwnScrollY);
4241 animateScroll();
4242 return true;
4243 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004244 }
4245 break;
4246 }
4247 return false;
4248 }
4249
4250 @ShadeViewRefactor(RefactorComponent.INPUT)
4251 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4252 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
4253 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
4254 View translatingParentView = mSwipeHelper.getTranslatingParentView();
4255 View view = null;
4256 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4257 // Only close visible guts if they're not a leavebehind.
4258 view = guts;
4259 } else if (menuRow != null && menuRow.isMenuVisible()
4260 && translatingParentView != null) {
4261 // Checking menu
4262 view = translatingParentView;
4263 }
4264 if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
4265 // Touch was outside visible guts / menu notification, close what's visible
4266 mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
4267 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4268 false /* resetMenu */);
4269 resetExposedMenuView(true /* animate */, true /* force */);
4270 }
4271 }
4272
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05004273 @ShadeViewRefactor(RefactorComponent.INPUT)
4274 private void setSwipingInProgress(boolean swiping) {
4275 mSwipingInProgress = swiping;
4276 if (swiping) {
4277 requestDisallowInterceptTouchEvent(true);
4278 }
4279 }
4280
Selim Cinek67b22602014-03-10 15:40:16 +01004281 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004282 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01004283 public void onWindowFocusChanged(boolean hasWindowFocus) {
4284 super.onWindowFocusChanged(hasWindowFocus);
4285 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004286 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01004287 }
4288 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004289
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07004290 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004291 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04004292 public void clearChildFocus(View child) {
4293 super.clearChildFocus(child);
4294 if (mForcedScroll == child) {
4295 mForcedScroll = null;
4296 }
4297 }
4298
Selim Cinekfab078b2014-03-27 22:45:58 +01004299 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004300 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfab078b2014-03-27 22:45:58 +01004301 public boolean isScrolledToTop() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004302 if (ANCHOR_SCROLLING) {
4303 updateScrollAnchor();
4304 // TODO: once we're recycling this will need to check the adapter position of the child
4305 return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0;
4306 } else {
4307 return mOwnScrollY == 0;
4308 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004309 }
4310
4311 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004312 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004313 public boolean isScrolledToBottom() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004314 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05004315 return getMaxPositiveScrollAmount() <= 0;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004316 } else {
4317 return mOwnScrollY >= getScrollRange();
4318 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004319 }
4320
4321 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004322 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfab078b2014-03-27 22:45:58 +01004323 public View getHostView() {
4324 return this;
4325 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004326
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004327 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004328 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08004329 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004330 }
4331
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004332 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004333 public void checkSnoozeLeavebehind() {
4334 if (mCheckForLeavebehind) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04004335 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
Eliot Courtney47098cb2017-10-18 17:30:30 +09004336 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4337 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004338 mCheckForLeavebehind = false;
4339 }
4340 }
4341
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004342 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004343 public void resetCheckSnoozeLeavebehind() {
4344 mCheckForLeavebehind = true;
4345 }
4346
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004347 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004348 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004349 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004350 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004351 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02004352 }
4353
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004354 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004355 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004356 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004357 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08004358 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02004359 if (!mIsExpanded) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004360 resetScrollPosition();
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004361 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004362 clearTemporaryViews();
4363 clearUserLockedViews();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004364 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08004365 if (draggedViews.size() > 0) {
4366 draggedViews.clear();
4367 updateContinuousShadowDrawing();
4368 }
Selim Cinek5b1591a2017-07-03 17:05:01 +02004369 }
4370 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01004371
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004372 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004373 private void clearUserLockedViews() {
4374 for (int i = 0; i < getChildCount(); i++) {
4375 ExpandableView child = (ExpandableView) getChildAt(i);
4376 if (child instanceof ExpandableNotificationRow) {
4377 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4378 row.setUserLocked(false);
4379 }
4380 }
4381 }
4382
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004383 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004384 private void clearTemporaryViews() {
Selim Cinek9dd0d042018-05-14 18:12:42 -07004385 // lets make sure nothing is transient anymore
Rohan Shah8ee53652018-04-05 11:13:50 -07004386 clearTemporaryViewsInGroup(this);
Selim Cinek5b1591a2017-07-03 17:05:01 +02004387 for (int i = 0; i < getChildCount(); i++) {
4388 ExpandableView child = (ExpandableView) getChildAt(i);
4389 if (child instanceof ExpandableNotificationRow) {
4390 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Rohan Shah8ee53652018-04-05 11:13:50 -07004391 clearTemporaryViewsInGroup(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07004392 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02004393 }
Selim Cinek1685e632014-04-08 02:27:49 +02004394 }
4395
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004396 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shah8ee53652018-04-05 11:13:50 -07004397 private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07004398 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04004399 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07004400 }
4401 }
4402
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004403 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004404 public void onPanelTrackingStarted() {
4405 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004406 mAmbientState.setPanelTracking(true);
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004407 resetExposedMenuView(true /* animate */, true /* force */);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004408 }
Jason Monke59dc402018-08-16 12:05:01 -04004409
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004410 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004411 public void onPanelTrackingStopped() {
4412 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004413 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004414 }
4415
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004416 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb24e0a92015-06-09 20:17:30 -07004417 public void resetScrollPosition() {
4418 mScroller.abortAnimation();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004419 if (ANCHOR_SCROLLING) {
4420 // TODO: once we're recycling this will need to modify the adapter position instead
4421 mScrollAnchorView = getFirstChildNotGone();
4422 mScrollAnchorViewY = 0;
4423 updateOnScrollChange();
4424 } else {
4425 setOwnScrollY(0);
4426 }
Selim Cinekb24e0a92015-06-09 20:17:30 -07004427 }
4428
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004429 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek3d6ae232019-01-04 14:14:33 -08004430 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02004431 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02004432 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02004433 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinek3d6ae232019-01-04 14:14:33 -08004434 mAmbientState.setShadeExpanded(isExpanded);
4435 mStateAnimator.setShadeExpanded(isExpanded);
shawnlin3a950c32019-05-15 20:06:10 +08004436 mSwipeHelper.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02004437 if (changed) {
Selim Cinekaae9ad62019-07-22 18:36:54 -07004438 mWillExpand = false;
Selim Cinek9184f9c2016-02-02 17:36:53 -08004439 if (!mIsExpanded) {
4440 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004441 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08004442 }
Selim Cinekcab4a602014-09-03 14:47:57 +02004443 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02004444 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01004445 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02004446 }
4447 }
4448
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004449 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek98713a42015-09-21 15:47:20 +02004450 private void updateChronometers() {
4451 int childCount = getChildCount();
4452 for (int i = 0; i < childCount; i++) {
4453 updateChronometerForChild(getChildAt(i));
4454 }
4455 }
4456
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004457 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek98713a42015-09-21 15:47:20 +02004458 private void updateChronometerForChild(View child) {
4459 if (child instanceof ExpandableNotificationRow) {
4460 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4461 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02004462 }
Selim Cinek1685e632014-04-08 02:27:49 +02004463 }
4464
Jorim Jaggibe565df2014-04-28 17:51:23 +02004465 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004466 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004467 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02004468 updateScrollPositionOnExpandInBottom(view);
4469 clampScrollPosition();
Lucas Dupin60661a62018-04-12 10:50:13 -07004470 notifyHeightChangeListener(view, needsAnimation);
Selim Cinekbc243a92016-09-27 16:35:13 -07004471 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
4472 ? (ExpandableNotificationRow) view
4473 : null;
Gus Prevase2d6f042018-10-17 15:25:30 -04004474 NotificationSection firstSection = getFirstVisibleSection();
4475 ActivatableNotificationView firstVisibleChild =
4476 firstSection == null ? null : firstSection.getFirstVisibleChild();
4477 if (row != null) {
4478 if (row == firstVisibleChild
4479 || row.getNotificationParent() == firstVisibleChild) {
4480 updateAlgorithmLayoutMinHeight();
4481 }
Selim Cinekbc243a92016-09-27 16:35:13 -07004482 }
Selim Cinekb5605e52015-02-20 18:21:41 +01004483 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08004484 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01004485 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004486 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02004487 }
4488
Selim Cineka5e211b2014-08-11 17:35:48 +02004489 @Override
4490 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02004491 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02004492 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02004493 }
4494
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004495 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf7a14c02014-07-07 14:01:46 +02004496 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07004497 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02004498 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004499 // TODO: once we're recycling this will need to check the adapter position of the child
Selim Cinekcb9400a2015-06-03 16:56:13 +02004500 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08004501 if (row.isSummaryWithChildren()) {
4502 return;
4503 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02004504 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08004505 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07004506 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08004507 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07004508 }
Selim Cinekdb167372016-11-17 15:41:17 -08004509 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Gus Prevase2d6f042018-10-17 15:25:30 -04004510 NotificationSection lastSection = getLastVisibleSection();
4511 ActivatableNotificationView lastVisibleChild =
4512 lastSection == null ? null : lastSection.getLastVisibleChild();
4513 if (row != lastVisibleChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08004514 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
4515 }
4516 if (endPosition > layoutEnd) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004517 if (ANCHOR_SCROLLING) {
4518 mScrollAnchorViewY -= (endPosition - layoutEnd);
4519 updateScrollAnchor();
4520 updateOnScrollChange();
4521 } else {
4522 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
4523 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02004524 mDisallowScrollingInThisMotion = true;
4525 }
4526 }
4527 }
4528 }
4529
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004530 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggibe565df2014-04-28 17:51:23 +02004531 public void setOnHeightChangedListener(
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004532 ExpandableView.OnHeightChangedListener onHeightChangedListener) {
4533 this.mOnHeightChangedListener = onHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004534 }
4535
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004536 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02004537 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08004538 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02004539 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07004540 runAnimationFinishedRunnables();
Selim Cinek9dd0d042018-05-14 18:12:42 -07004541 clearTransient();
Selim Cinek8fc78752016-07-13 14:34:56 -07004542 clearHeadsUpDisappearRunning();
4543 }
4544
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004545 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8fc78752016-07-13 14:34:56 -07004546 private void clearHeadsUpDisappearRunning() {
4547 for (int i = 0; i < getChildCount(); i++) {
4548 View view = getChildAt(i);
4549 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07004550 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07004551 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07004552 if (row.isSummaryWithChildren()) {
4553 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004554 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07004555 }
4556 }
Selim Cinek8fc78752016-07-13 14:34:56 -07004557 }
4558 }
Selim Cinek0fccc722015-07-29 17:04:36 -07004559 }
4560
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004561 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9dd0d042018-05-14 18:12:42 -07004562 private void clearTransient() {
4563 for (ExpandableView view : mClearTransientViewsWhenFinished) {
4564 StackStateAnimator.removeTransientView(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07004565 }
Selim Cinek9dd0d042018-05-14 18:12:42 -07004566 mClearTransientViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07004567 }
4568
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004569 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek32a59fd32015-06-10 13:54:42 -07004570 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004571 for (Runnable runnable : mAnimationFinishedRunnables) {
4572 runnable.run();
4573 }
4574 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02004575 }
4576
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004577 /**
4578 * See {@link AmbientState#setDimmed}.
4579 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004580 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004581 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07004582 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004583 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02004584 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004585 mDimmedNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004586 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08004587 animateDimmed(dimmed);
4588 } else {
4589 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004590 }
4591 requestChildrenUpdate();
4592 }
4593
Selim Cinek8a9308d2017-08-24 09:31:08 -07004594 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004595 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8a9308d2017-08-24 09:31:08 -07004596 boolean isDimmed() {
4597 return mAmbientState.isDimmed();
4598 }
4599
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004600 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd35c2792016-01-21 13:20:57 -08004601 private void setDimAmount(float dimAmount) {
4602 mDimAmount = dimAmount;
4603 updateBackgroundDimming();
4604 }
4605
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004606 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd35c2792016-01-21 13:20:57 -08004607 private void animateDimmed(boolean dimmed) {
4608 if (mDimAnimator != null) {
4609 mDimAnimator.cancel();
4610 }
4611 float target = dimmed ? 1.0f : 0.0f;
4612 if (target == mDimAmount) {
4613 return;
4614 }
4615 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
4616 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
4617 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
4618 mDimAnimator.addListener(mDimEndListener);
4619 mDimAnimator.addUpdateListener(mDimUpdateListener);
4620 mDimAnimator.start();
4621 }
Evan Laird91d0f102018-09-18 17:39:55 -04004622
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004623 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin28790c02020-01-30 14:18:41 -08004624 private void updateSensitiveness(boolean animate) {
4625 boolean hideSensitive = mLockscreenUserManager.isAnyProfilePublicMode();
Jorim Jaggiae441282014-08-01 02:45:18 +02004626 if (hideSensitive != mAmbientState.isHideSensitive()) {
4627 int childCount = getChildCount();
4628 for (int i = 0; i < childCount; i++) {
4629 ExpandableView v = (ExpandableView) getChildAt(i);
4630 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
4631 }
4632 mAmbientState.setHideSensitive(hideSensitive);
4633 if (animate && mAnimationsEnabled) {
4634 mHideSensitiveNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004635 mNeedsAnimation = true;
Jorim Jaggiae441282014-08-01 02:45:18 +02004636 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08004637 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02004638 requestChildrenUpdate();
4639 }
4640 }
4641
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004642 /**
4643 * See {@link AmbientState#setActivatedChild}.
4644 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004645 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka32ab602014-06-11 15:06:01 +02004646 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004647 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02004648 if (mAnimationsEnabled) {
4649 mActivateNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004650 mNeedsAnimation = true;
Jorim Jaggi75c95042014-05-16 19:09:59 +02004651 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004652 requestChildrenUpdate();
4653 }
4654
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004655 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka32ab602014-06-11 15:06:01 +02004656 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004657 return mAmbientState.getActivatedChild();
4658 }
4659
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004660 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02004661 private void applyCurrentState() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004662 int numChildren = getChildCount();
4663 for (int i = 0; i < numChildren; i++) {
4664 ExpandableView child = (ExpandableView) getChildAt(i);
4665 child.applyViewState();
4666 }
4667
Selim Cinekf4c19962014-05-01 21:55:31 +02004668 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004669 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02004670 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07004671 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07004672 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08004673 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08004674 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08004675 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08004676 }
4677
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004678 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08004679 private void updateViewShadows() {
4680 // we need to work around an issue where the shadow would not cast between siblings when
4681 // their z difference is between 0 and 0.1
4682
4683 // Lefts first sort by Z difference
4684 for (int i = 0; i < getChildCount(); i++) {
4685 ExpandableView child = (ExpandableView) getChildAt(i);
4686 if (child.getVisibility() != GONE) {
4687 mTmpSortedChildren.add(child);
4688 }
4689 }
4690 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
4691
4692 // Now lets update the shadow for the views
4693 ExpandableView previous = null;
4694 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
4695 ExpandableView expandableView = mTmpSortedChildren.get(i);
4696 float translationZ = expandableView.getTranslationZ();
4697 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
4698 float diff = otherZ - translationZ;
4699 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
4700 // There is no fake shadow to be drawn
4701 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
4702 } else {
4703 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07004704 expandableView.getTranslationY() - previous.getExtraBottomPadding();
4705 expandableView.setFakeShadowIntensity(
4706 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08004707 previous.getOutlineAlpha(), (int) yLocation,
4708 previous.getOutlineTranslation());
4709 }
4710 previous = expandableView;
4711 }
4712
4713 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02004714 }
4715
Lucas Dupine17ce522017-07-17 15:45:06 -07004716 /**
4717 * Update colors of "dismiss" and "empty shade" views.
4718 *
4719 * @param lightTheme True if light theme should be used.
4720 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004721 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupine17ce522017-07-17 15:45:06 -07004722 public void updateDecorViews(boolean lightTheme) {
4723 if (lightTheme == mUsingLightTheme) {
4724 return;
4725 }
4726 mUsingLightTheme = lightTheme;
4727 Context context = new ContextThemeWrapper(mContext,
4728 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
Jason Changb4e879d2018-04-11 11:17:58 +08004729 final int textColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004730 mFooterView.setTextColor(textColor);
Lucas Dupine17ce522017-07-17 15:45:06 -07004731 mEmptyShadeView.setTextColor(textColor);
4732 }
4733
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004734 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004735 public void goToFullShade(long delay) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004736 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004737 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004738 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004739 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02004740 }
4741
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004742 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02004743 public void cancelExpandHelper() {
4744 mExpandHelper.cancel();
4745 }
4746
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004747 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02004748 public void setIntrinsicPadding(int intrinsicPadding) {
4749 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07004750 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02004751 }
4752
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004753 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02004754 public int getIntrinsicPadding() {
4755 return mIntrinsicPadding;
4756 }
4757
Selim Cinekc0ce82d2014-06-10 13:21:15 +02004758 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004759 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc0ce82d2014-06-10 13:21:15 +02004760 public boolean shouldDelayChildPressedState() {
4761 return true;
4762 }
4763
Jorim Jaggi457cc352014-06-02 22:47:42 +02004764 /**
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004765 * See {@link AmbientState#setDozing}.
John Spurlockbf370992014-06-17 13:58:31 -04004766 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004767 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004768 public void setDozing(boolean dozing, boolean animate,
4769 @Nullable PointF touchWakeUpScreenLocation) {
4770 if (mAmbientState.isDozing() == dozing) {
Adrian Roos260c1f72017-08-07 15:52:26 +02004771 return;
4772 }
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004773 mAmbientState.setDozing(dozing);
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004774 requestChildrenUpdate();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004775 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04004776 }
4777
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004778 /**
Selim Cinek195dfc52019-05-30 19:35:05 -07004779 * Sets the current hide amount.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004780 *
Selim Cinek195dfc52019-05-30 19:35:05 -07004781 * @param linearHideAmount The hide amount that follows linear interpoloation in the
Jason Monke59dc402018-08-16 12:05:01 -04004782 * animation,
4783 * i.e. animates from 0 to 1 or vice-versa in a linear manner.
Selim Cinek195dfc52019-05-30 19:35:05 -07004784 * @param interpolatedHideAmount The hide amount that follows the actual interpolation of the
Jason Monke59dc402018-08-16 12:05:01 -04004785 * animation curve.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004786 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004787 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek195dfc52019-05-30 19:35:05 -07004788 public void setHideAmount(float linearHideAmount, float interpolatedHideAmount) {
4789 mLinearHideAmount = linearHideAmount;
4790 mInterpolatedHideAmount = interpolatedHideAmount;
4791 boolean wasFullyHidden = mAmbientState.isFullyHidden();
4792 boolean wasHiddenAtAll = mAmbientState.isHiddenAtAll();
4793 mAmbientState.setHideAmount(interpolatedHideAmount);
4794 boolean nowFullyHidden = mAmbientState.isFullyHidden();
4795 boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
4796 if (nowFullyHidden != wasFullyHidden) {
Selim Cinekd09ec7e2019-06-26 11:34:57 -07004797 updateVisibility();
Lucas Dupin16cfe452018-02-08 13:14:50 -08004798 }
Selim Cinek195dfc52019-05-30 19:35:05 -07004799 if (!wasHiddenAtAll && nowHiddenAtAll) {
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004800 resetExposedMenuView(true /* animate */, true /* animate */);
4801 }
Selim Cinek195dfc52019-05-30 19:35:05 -07004802 if (nowFullyHidden != wasFullyHidden || wasHiddenAtAll != nowHiddenAtAll) {
Lucas Dupin64e2f572019-03-21 14:21:14 -07004803 invalidateOutline();
4804 }
Lucas Dupin60661a62018-04-12 10:50:13 -07004805 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07004806 updateBackgroundDimming();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004807 requestChildrenUpdate();
Selim Cinek33295a42019-07-26 19:49:08 -07004808 updateOwnTranslationZ();
4809 }
4810
4811 private void updateOwnTranslationZ() {
4812 // Since we are clipping to the outline we need to make sure that the shadows aren't
4813 // clipped when pulsing
4814 float ownTranslationZ = 0;
4815 if (mKeyguardBypassController.getBypassEnabled() && mAmbientState.isHiddenAtAll()) {
4816 ExpandableView firstChildNotGone = getFirstChildNotGone();
4817 if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) {
4818 ownTranslationZ = firstChildNotGone.getTranslationZ();
4819 }
4820 }
4821 setTranslationZ(ownTranslationZ);
Selim Cinek972123d2016-05-03 14:25:58 -07004822 }
4823
Selim Cinekd09ec7e2019-06-26 11:34:57 -07004824 private void updateVisibility() {
4825 boolean shouldShow = !mAmbientState.isFullyHidden() || !onKeyguard();
4826 setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
4827 }
4828
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004829 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek195dfc52019-05-30 19:35:05 -07004830 public void notifyHideAnimationStart(boolean hide) {
4831 // We only swap the scaling factor if we're fully hidden or fully awake to avoid
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004832 // interpolation issues when playing with the power button.
Selim Cinek195dfc52019-05-30 19:35:05 -07004833 if (mInterpolatedHideAmount == 0 || mInterpolatedHideAmount == 1) {
4834 mBackgroundXFactor = hide ? 1.8f : 1.5f;
4835 mHideXInterpolator = hide
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004836 ? Interpolators.FAST_OUT_SLOW_IN_REVERSE
4837 : Interpolators.FAST_OUT_SLOW_IN;
4838 }
Selim Cinek972123d2016-05-03 14:25:58 -07004839 }
4840
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004841 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004842 private int getNotGoneIndex(View child) {
4843 int count = getChildCount();
4844 int notGoneIndex = 0;
4845 for (int i = 0; i < count; i++) {
4846 View v = getChildAt(i);
4847 if (child == v) {
4848 return notGoneIndex;
4849 }
4850 if (v.getVisibility() != View.GONE) {
4851 notGoneIndex++;
4852 }
4853 }
4854 return -1;
4855 }
4856
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004857 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004858 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004859 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004860 if (mFooterView != null) {
4861 index = indexOfChild(mFooterView);
4862 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004863 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004864 mFooterView = footerView;
4865 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004866 }
4867
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004868 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004869 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004870 int index = -1;
4871 if (mEmptyShadeView != null) {
4872 index = indexOfChild(mEmptyShadeView);
4873 removeView(mEmptyShadeView);
4874 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004875 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004876 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004877 }
4878
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004879 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004880 public void updateEmptyShadeView(boolean visible) {
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004881 mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
Lucas Dupinc9274ff2018-05-09 17:40:20 -07004882
4883 int oldTextRes = mEmptyShadeView.getTextResource();
Beverly482ad6a2019-11-06 16:57:05 -05004884 int newTextRes = mZenController.areNotificationsHiddenInShade()
Lucas Dupinc9274ff2018-05-09 17:40:20 -07004885 ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
4886 if (oldTextRes != newTextRes) {
4887 mEmptyShadeView.setText(newTextRes);
4888 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004889 }
4890
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004891 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004892 public void updateFooterView(boolean visible, boolean showDismissView) {
4893 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004894 return;
4895 }
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004896 boolean animate = mIsExpanded && mAnimationsEnabled;
4897 mFooterView.setVisible(visible, animate);
4898 mFooterView.setSecondaryVisible(showDismissView, animate);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004899 }
4900
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004901 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dan Sandlereceda3d2014-07-21 15:35:01 -04004902 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4903 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004904 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004905 handleDismissAllClipping();
4906 }
4907
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004908 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek9c17b772015-07-07 20:37:09 -07004909 private void handleDismissAllClipping() {
4910 final int count = getChildCount();
4911 boolean previousChildWillBeDismissed = false;
4912 for (int i = 0; i < count; i++) {
4913 ExpandableView child = (ExpandableView) getChildAt(i);
4914 if (child.getVisibility() == GONE) {
4915 continue;
4916 }
4917 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4918 child.setMinClipTopAmount(child.getClipTopAmount());
4919 } else {
4920 child.setMinClipTopAmount(0);
4921 }
Steve Elliott946b00c2019-10-28 11:20:24 -04004922 previousChildWillBeDismissed = canChildBeDismissed(child);
Selim Cinek9c17b772015-07-07 20:37:09 -07004923 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004924 }
4925
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004926 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004927 public boolean isFooterViewNotGone() {
4928 return mFooterView != null
4929 && mFooterView.getVisibility() != View.GONE
4930 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004931 }
4932
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004933 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek38475442018-05-18 11:11:46 -07004934 public boolean isFooterViewContentVisible() {
4935 return mFooterView != null && mFooterView.isContentVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004936 }
4937
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004938 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004939 public int getFooterViewHeight() {
4940 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004941 }
4942
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004943 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004944 public int getEmptyShadeViewHeight() {
4945 return mEmptyShadeView.getHeight();
4946 }
4947
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004948 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004949 public float getBottomMostNotificationBottom() {
4950 final int count = getChildCount();
4951 float max = 0;
4952 for (int childIdx = 0; childIdx < count; childIdx++) {
4953 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4954 if (child.getVisibility() == GONE) {
4955 continue;
4956 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004957 float bottom = child.getTranslationY() + child.getActualHeight()
4958 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004959 if (bottom > max) {
4960 max = bottom;
4961 }
4962 }
Selim Cinekd2281152015-04-10 14:37:46 -07004963 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004964 }
4965
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004966 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004967 public void setStatusBar(StatusBar statusBar) {
4968 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004969 }
4970
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004971 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb5605e52015-02-20 18:21:41 +01004972 public void setGroupManager(NotificationGroupManager groupManager) {
4973 this.mGroupManager = groupManager;
Kevin01a53cb2018-11-09 18:19:54 -08004974 mGroupManager.addOnGroupChangeListener(mOnGroupChangeListener);
Selim Cinek379ff8f2015-02-20 17:03:16 +01004975 }
4976
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004977 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek379ff8f2015-02-20 17:03:16 +01004978 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004979 if (mIsExpanded && mAnimationsEnabled) {
4980 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004981 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004982 requestChildrenUpdate();
4983 }
4984 }
4985
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004986 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek04fb2582015-06-02 19:58:09 +02004987 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004988 int childCount = getChildCount();
4989 for (int i = childCount - 1; i >= 0; i--) {
4990 ExpandableView child = (ExpandableView) getChildAt(i);
4991 if (child.getVisibility() != View.GONE) {
4992 float childTop = child.getY();
4993 if (childTop > touchY) {
4994 // we are above a notification entirely let's abort
4995 return false;
4996 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004997 boolean belowChild = touchY > childTop + child.getActualHeight()
4998 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004999 if (child == mFooterView) {
Jason Monke59dc402018-08-16 12:05:01 -04005000 if (!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
5001 touchY - childTop)) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01005002 // We clicked on the dismiss button
5003 return false;
5004 }
5005 } else if (child == mEmptyShadeView) {
5006 // We arrived at the empty shade view, for which we accept all clicks
5007 return true;
Jason Monke59dc402018-08-16 12:05:01 -04005008 } else if (!belowChild) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01005009 // We are on a child
5010 return false;
5011 }
5012 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005013 }
Selim Cinek04fb2582015-06-02 19:58:09 +02005014 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005015 }
5016
Selim Cinekc22fff62016-05-20 12:44:30 -07005017 /** @hide */
5018 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005019 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07005020 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
5021 super.onInitializeAccessibilityEventInternal(event);
5022 event.setScrollable(mScrollable);
Selim Cinekc22fff62016-05-20 12:44:30 -07005023 event.setMaxScrollX(mScrollX);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005024 if (ANCHOR_SCROLLING) {
5025 // TODO
5026 } else {
5027 event.setScrollY(mOwnScrollY);
5028 event.setMaxScrollY(getScrollRange());
5029 }
Selim Cinekc22fff62016-05-20 12:44:30 -07005030 }
5031
5032 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005033 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07005034 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
5035 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07005036 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005037 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07005038 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005039 info.addAction(
5040 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
5041 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
5042 }
Selim Cinekef406062016-09-29 17:33:13 -07005043 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005044 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
5045 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
5046 }
5047 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07005048 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
5049 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07005050 }
5051
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005052 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb5605e52015-02-20 18:21:41 +01005053 public void generateChildOrderChangedEvent() {
5054 if (mIsExpanded && mAnimationsEnabled) {
5055 mGenerateChildOrderChangedEvent = true;
5056 mNeedsAnimation = true;
5057 requestChildrenUpdate();
5058 }
5059 }
5060
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005061 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005062 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005063 public int getContainerChildCount() {
5064 return getChildCount();
5065 }
5066
5067 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005068 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005069 public View getContainerChildAt(int i) {
5070 return getChildAt(i);
5071 }
5072
5073 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005074 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005075 public void removeContainerView(View v) {
Ned Burnsd4a69f72019-06-19 19:49:19 -04005076 Assert.isMainThread();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005077 removeView(v);
5078 }
5079
5080 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005081 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005082 public void addContainerView(View v) {
Ned Burnsd4a69f72019-06-19 19:49:19 -04005083 Assert.isMainThread();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005084 addView(v);
5085 }
5086
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005087 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek684a4422015-04-15 16:18:39 -07005088 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005089 mAnimationFinishedRunnables.add(runnable);
5090 }
5091
Ned Burnsf81c4c42019-01-07 14:10:43 -05005092 public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
Evan Laird94492852018-10-25 13:43:01 -04005093 ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
5094 generateHeadsUpAnimation(row, isHeadsUp);
5095 }
5096
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005097 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005098 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07005099 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005100 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
5101 mNeedsAnimation = true;
Selim Cinekaae9ad62019-07-22 18:36:54 -07005102 if (!mIsExpanded && !mWillExpand && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07005103 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07005104 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005105 requestChildrenUpdate();
5106 }
5107 }
5108
Selim Cineka59ecc32015-04-07 10:51:49 -07005109 /**
5110 * Set the boundary for the bottom heads up position. The heads up will always be above this
5111 * position.
5112 *
Jason Monke59dc402018-08-16 12:05:01 -04005113 * @param height the height of the screen
Selim Cineka59ecc32015-04-07 10:51:49 -07005114 * @param bottomBarHeight the height of the bar on the bottom
5115 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005116 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka59ecc32015-04-07 10:51:49 -07005117 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
5118 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
5119 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005120 requestChildrenUpdate();
5121 }
5122
Selim Cinekaae9ad62019-07-22 18:36:54 -07005123 @Override
5124 public void setWillExpand(boolean willExpand) {
5125 mWillExpand = willExpand;
5126 }
5127
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005128 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005129 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
5130 mTrackingHeadsUp = row != null;
5131 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005132 }
5133
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005134 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaac93252015-04-14 20:04:12 -07005135 public void setScrimController(ScrimController scrimController) {
5136 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07005137 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07005138 }
5139
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005140 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005141 public void forceNoOverlappingRendering(boolean force) {
5142 mForceNoOverlappingRendering = force;
5143 }
5144
5145 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005146 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005147 public boolean hasOverlappingRendering() {
5148 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
5149 }
5150
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005151 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek6811d722016-01-19 17:53:12 -08005152 public void setAnimationRunning(boolean animationRunning) {
5153 if (animationRunning != mAnimationRunning) {
5154 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07005155 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005156 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07005157 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005158 }
5159 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08005160 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08005161 }
5162 }
5163
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005164 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3776fe02016-02-04 13:32:43 -08005165 public boolean isExpanded() {
5166 return mIsExpanded;
5167 }
5168
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005169 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin4e023812018-04-02 21:19:23 -07005170 public void setPulsing(boolean pulsing, boolean animated) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09005171 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01005172 return;
5173 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005174 mPulsing = pulsing;
Selim Cinekebf42342017-07-13 15:46:10 +02005175 mAmbientState.setPulsing(pulsing);
Selim Cinekd0b48e32019-05-24 20:49:23 -07005176 mSwipeHelper.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005177 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08005178 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08005179 updateContentHeight();
Adrian Roosd83e9992017-03-16 15:17:57 -07005180 requestChildrenUpdate();
Lucas Dupin4e023812018-04-02 21:19:23 -07005181 notifyHeightChangeListener(null, animated);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005182 }
5183
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005184 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07005185 public void setQsExpanded(boolean qsExpanded) {
5186 mQsExpanded = qsExpanded;
5187 updateAlgorithmLayoutMinHeight();
Riddle Hsu065c01c2018-05-10 23:14:19 +08005188 updateScrollability();
Selim Cinekbc243a92016-09-27 16:35:13 -07005189 }
5190
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005191 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005192 public void setQsExpansionFraction(float qsExpansionFraction) {
5193 mQsExpansionFraction = qsExpansionFraction;
5194 }
5195
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005196 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Gus Prevas0fa58d62019-01-11 13:58:40 -05005197 private void setOwnScrollY(int ownScrollY) {
5198 assert !ANCHOR_SCROLLING;
Selim Cinekef406062016-09-29 17:33:13 -07005199 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08005200 // We still want to call the normal scrolled changed for accessibility reasons
5201 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07005202 mOwnScrollY = ownScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -05005203 updateOnScrollChange();
5204 }
5205 }
5206
5207 private void updateOnScrollChange() {
5208 updateForwardAndBackwardScrollability();
5209 requestChildrenUpdate();
5210 }
5211
5212 private void updateScrollAnchor() {
5213 int anchorIndex = indexOfChild(mScrollAnchorView);
5214 // If the anchor view has been scrolled off the top, move to the next view.
5215 while (mScrollAnchorViewY < 0) {
5216 View nextAnchor = null;
5217 for (int i = anchorIndex + 1; i < getChildCount(); i++) {
5218 View child = getChildAt(i);
5219 if (child.getVisibility() != View.GONE
5220 && child instanceof ExpandableNotificationRow) {
5221 anchorIndex = i;
5222 nextAnchor = child;
5223 break;
5224 }
5225 }
5226 if (nextAnchor == null) {
5227 break;
5228 }
5229 mScrollAnchorViewY +=
5230 (int) (nextAnchor.getTranslationY() - mScrollAnchorView.getTranslationY());
5231 mScrollAnchorView = nextAnchor;
5232 }
5233 // If the view above the anchor view is fully visible, make it the anchor view.
5234 while (anchorIndex > 0 && mScrollAnchorViewY > 0) {
5235 View prevAnchor = null;
5236 for (int i = anchorIndex - 1; i >= 0; i--) {
5237 View child = getChildAt(i);
5238 if (child.getVisibility() != View.GONE
5239 && child instanceof ExpandableNotificationRow) {
5240 anchorIndex = i;
5241 prevAnchor = child;
5242 break;
5243 }
5244 }
5245 if (prevAnchor == null) {
5246 break;
5247 }
5248 float distanceToPreviousAnchor =
5249 mScrollAnchorView.getTranslationY() - prevAnchor.getTranslationY();
5250 if (distanceToPreviousAnchor < mScrollAnchorViewY) {
5251 mScrollAnchorViewY -= (int) distanceToPreviousAnchor;
5252 mScrollAnchorView = prevAnchor;
5253 }
Selim Cinekef406062016-09-29 17:33:13 -07005254 }
5255 }
5256
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005257 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005258 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07005259 int index = -1;
5260 if (mShelf != null) {
5261 index = indexOfChild(mShelf);
5262 removeView(mShelf);
5263 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08005264 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07005265 addView(mShelf, index);
5266 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07005267 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07005268 shelf.bind(mAmbientState, this);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005269 if (ANCHOR_SCROLLING) {
5270 mScrollAnchorView = mShelf;
5271 }
Selim Cinek281c2022016-10-13 19:14:43 -07005272 }
5273
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005274 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005275 public NotificationShelf getNotificationShelf() {
5276 return mShelf;
5277 }
5278
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005279 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekad7fac02016-10-18 17:09:15 -07005280 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
5281 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
5282 mMaxDisplayedNotifications = maxDisplayedNotifications;
5283 updateContentHeight();
5284 notifyHeightChangeListener(mShelf);
5285 }
5286 }
5287
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005288 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005289 public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
Jason Monke59dc402018-08-16 12:05:01 -04005290 mShouldShowShelfOnly = shouldShowShelfOnly;
shawnlin8e4e92c2018-04-12 18:47:24 +08005291 updateAlgorithmLayoutMinHeight();
5292 }
5293
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005294 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek48ff9b42016-11-09 19:31:51 -08005295 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07005296 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08005297 }
5298
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005299 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005300 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
5301 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
5302 updateClipping();
5303 }
5304
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005305 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005306 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
5307 mHeadsUpAnimatingAway = headsUpAnimatingAway;
5308 updateClipping();
5309 }
5310
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005311 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk297c04e2018-08-23 17:16:59 -04005312 @VisibleForTesting
5313 protected void setStatusBarState(int statusBarState) {
Selim Cinek355652a2016-12-07 13:32:12 -08005314 mStatusBarState = statusBarState;
5315 mAmbientState.setStatusBarState(statusBarState);
Evan Laird91d0f102018-09-18 17:39:55 -04005316 }
5317
5318 private void onStatePostChange() {
Jason Monk1fd3fc32018-08-14 17:20:09 -04005319 boolean onKeyguard = onKeyguard();
Evan Laird91d0f102018-09-18 17:39:55 -04005320
Jason Monk1fd3fc32018-08-14 17:20:09 -04005321 if (mHeadsUpAppearanceController != null) {
Selim Cinekb2c5dc52019-06-24 15:46:52 -07005322 mHeadsUpAppearanceController.onStateChanged();
Jason Monk1fd3fc32018-08-14 17:20:09 -04005323 }
5324
Beverly8fdb5332019-02-04 14:29:49 -05005325 SysuiStatusBarStateController state = (SysuiStatusBarStateController)
5326 Dependency.get(StatusBarStateController.class);
Lucas Dupin28790c02020-01-30 14:18:41 -08005327 updateSensitiveness(state.goingToFullShade() /* animate */);
Jason Monk1fd3fc32018-08-14 17:20:09 -04005328 setDimmed(onKeyguard, state.fromShadeLocked() /* animate */);
5329 setExpandingEnabled(!onKeyguard);
5330 ActivatableNotificationView activatedChild = getActivatedChild();
5331 setActivatedChild(null);
5332 if (activatedChild != null) {
5333 activatedChild.makeInactive(false /* animate */);
5334 }
Jason Monke59dc402018-08-16 12:05:01 -04005335 updateFooter();
Dave Mankoff57445802018-10-10 14:47:34 -04005336 requestChildrenUpdate();
Jason Monke59dc402018-08-16 12:05:01 -04005337 onUpdateRowStates();
Evan Laird91d0f102018-09-18 17:39:55 -04005338
Beverly85d4c192019-09-30 11:40:39 -04005339 mEntryManager.updateNotifications("StatusBar state changed");
Selim Cinekd09ec7e2019-06-26 11:34:57 -07005340 updateVisibility();
Selim Cinek355652a2016-12-07 13:32:12 -08005341 }
5342
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005343 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd5ab6452016-12-08 16:34:00 -08005344 public void setExpandingVelocity(float expandingVelocity) {
5345 mAmbientState.setExpandingVelocity(expandingVelocity);
5346 }
5347
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005348 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005349 public float getOpeningHeight() {
5350 if (mEmptyShadeView.getVisibility() == GONE) {
5351 return getMinExpansionHeight();
5352 } else {
5353 return getAppearEndPosition();
5354 }
5355 }
5356
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005357 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005358 public void setIsFullWidth(boolean isFullWidth) {
5359 mAmbientState.setPanelFullWidth(isFullWidth);
5360 }
5361
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005362 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekec29d342017-05-05 18:31:49 -07005363 public void setUnlockHintRunning(boolean running) {
5364 mAmbientState.setUnlockHintRunning(running);
5365 }
5366
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005367 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005368 public void setQsCustomizerShowing(boolean isShowing) {
5369 mAmbientState.setQsCustomizerShowing(isShowing);
5370 requestChildrenUpdate();
5371 }
5372
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005373 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005374 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
5375 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
5376 }
5377
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005378 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek707e2072017-06-30 18:32:40 +02005379 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5380 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
shawnlin8e4e92c2018-04-12 18:47:24 +08005381 + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
5382 + " qsExpandFraction=%f]",
Selim Cinek707e2072017-06-30 18:32:40 +02005383 this.getClass().getSimpleName(),
Jason Monke59dc402018-08-16 12:05:01 -04005384 mPulsing ? "T" : "f",
5385 mAmbientState.isQsCustomizerShowing() ? "T" : "f",
Selim Cinek707e2072017-06-30 18:32:40 +02005386 getVisibility() == View.VISIBLE ? "visible"
5387 : getVisibility() == View.GONE ? "gone"
5388 : "invisible",
5389 getAlpha(),
shawnlin8e4e92c2018-04-12 18:47:24 +08005390 mAmbientState.getScrollY(),
5391 mMaxTopPadding,
Jason Monke59dc402018-08-16 12:05:01 -04005392 mShouldShowShelfOnly ? "T" : "f",
shawnlin8e4e92c2018-04-12 18:47:24 +08005393 mQsExpansionFraction));
Selim Cinek30887662018-10-15 17:37:21 -07005394 int childCount = getChildCount();
5395 pw.println(" Number of children: " + childCount);
5396 pw.println();
5397
5398 for (int i = 0; i < childCount; i++) {
5399 ExpandableView child = (ExpandableView) getChildAt(i);
5400 child.dump(fd, pw, args);
5401 if (!(child instanceof ExpandableNotificationRow)) {
5402 pw.println(" " + child.getClass().getSimpleName());
5403 // Notifications dump it's viewstate as part of their dump to support children
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005404 ExpandableViewState viewState = child.getViewState();
Selim Cinek30887662018-10-15 17:37:21 -07005405 if (viewState == null) {
5406 pw.println(" no viewState!!!");
5407 } else {
5408 pw.print(" ");
5409 viewState.dump(fd, pw, args);
5410 pw.println();
5411 pw.println();
5412 }
5413 }
5414 }
Selim Cinek30887662018-10-15 17:37:21 -07005415 int transientViewCount = getTransientViewCount();
Selim Cinekd4c32302018-11-19 19:43:14 -08005416 pw.println(" Transient Views: " + transientViewCount);
Selim Cinek30887662018-10-15 17:37:21 -07005417 for (int i = 0; i < transientViewCount; i++) {
5418 ExpandableView child = (ExpandableView) getTransientView(i);
5419 child.dump(fd, pw, args);
5420 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005421 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08005422 int draggedCount = draggedViews.size();
5423 pw.println(" Dragged Views: " + draggedCount);
5424 for (int i = 0; i < draggedCount; i++) {
5425 ExpandableView child = (ExpandableView) draggedViews.get(i);
5426 child.dump(fd, pw, args);
5427 }
Selim Cinek707e2072017-06-30 18:32:40 +02005428 }
5429
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005430 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek195dfc52019-05-30 19:35:05 -07005431 public boolean isFullyHidden() {
5432 return mAmbientState.isFullyHidden();
Lucas Dupin16cfe452018-02-08 13:14:50 -08005433 }
5434
Selim Cinek7103fd42016-05-09 22:22:33 -04005435 /**
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005436 * Add a listener whenever the expanded height changes. The first value passed as an
5437 * argument is the expanded height and the second one is the appearFraction.
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005438 *
5439 * @param listener the listener to notify.
5440 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005441 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005442 public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005443 mExpandedHeightListeners.add(listener);
5444 }
5445
5446 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07005447 * Stop a listener from listening to the expandedHeight.
5448 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005449 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005450 public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
Selim Cinek60ffea62018-03-22 13:16:44 -07005451 mExpandedHeightListeners.remove(listener);
5452 }
5453
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005454 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekf0c79e12018-05-14 17:17:31 -07005455 public void setHeadsUpAppearanceController(
5456 HeadsUpAppearanceController headsUpAppearanceController) {
5457 mHeadsUpAppearanceController = headsUpAppearanceController;
5458 }
5459
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005460 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek9bfc7a52018-06-11 16:09:00 -07005461 public void setIconAreaController(NotificationIconAreaController controller) {
5462 mIconAreaController = controller;
5463 }
5464
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005465 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005466 public void manageNotifications(View v) {
5467 Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
5468 mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
5469 }
5470
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005471 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -04005472 private void clearNotifications(
5473 @SelectedRows int selection,
5474 boolean closeShade) {
Jason Monke59dc402018-08-16 12:05:01 -04005475 // animate-swipe all dismissable notifications, then animate the shade closed
5476 int numChildren = getChildCount();
5477
5478 final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
5479 final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
5480 for (int i = 0; i < numChildren; i++) {
5481 final View child = getChildAt(i);
5482 if (child instanceof ExpandableNotificationRow) {
5483 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
5484 boolean parentVisible = false;
5485 boolean hasClipBounds = child.getClipBounds(mTmpRect);
Ned Burns61269442019-05-02 18:27:23 -04005486 if (includeChildInDismissAll(row, selection)) {
Jason Monke59dc402018-08-16 12:05:01 -04005487 viewsToRemove.add(row);
5488 if (child.getVisibility() == View.VISIBLE
5489 && (!hasClipBounds || mTmpRect.height() > 0)) {
5490 viewsToHide.add(child);
5491 parentVisible = true;
5492 }
5493 } else if (child.getVisibility() == View.VISIBLE
5494 && (!hasClipBounds || mTmpRect.height() > 0)) {
5495 parentVisible = true;
5496 }
5497 List<ExpandableNotificationRow> children = row.getNotificationChildren();
5498 if (children != null) {
5499 for (ExpandableNotificationRow childRow : children) {
Ned Burns61269442019-05-02 18:27:23 -04005500 if (includeChildInDismissAll(row, selection)) {
5501 viewsToRemove.add(childRow);
5502 if (parentVisible && row.areChildrenExpanded()) {
5503 hasClipBounds = childRow.getClipBounds(mTmpRect);
5504 if (childRow.getVisibility() == View.VISIBLE
5505 && (!hasClipBounds || mTmpRect.height() > 0)) {
5506 viewsToHide.add(childRow);
5507 }
Jason Monke59dc402018-08-16 12:05:01 -04005508 }
5509 }
5510 }
5511 }
5512 }
5513 }
Ned Burns61269442019-05-02 18:27:23 -04005514
Jason Monke59dc402018-08-16 12:05:01 -04005515 if (viewsToRemove.isEmpty()) {
Ned Burns61269442019-05-02 18:27:23 -04005516 if (closeShade) {
Dave Mankoffaf8163f2020-01-08 14:24:35 -05005517 Dependency.get(ShadeController.class).animateCollapsePanels(
5518 CommandQueue.FLAG_EXCLUDE_NONE);
Ned Burns61269442019-05-02 18:27:23 -04005519 }
Jason Monke59dc402018-08-16 12:05:01 -04005520 return;
5521 }
5522
Ned Burns61269442019-05-02 18:27:23 -04005523 performDismissAllAnimations(viewsToHide, closeShade, () -> {
Jason Monke59dc402018-08-16 12:05:01 -04005524 for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
Steve Elliott946b00c2019-10-28 11:20:24 -04005525 if (canChildBeDismissed(rowToRemove)) {
Ned Burns61269442019-05-02 18:27:23 -04005526 if (selection == ROWS_ALL) {
5527 // TODO: This is a listener method; we shouldn't be calling it. Can we just
5528 // call performRemoveNotification as below?
5529 mEntryManager.removeNotification(
Ned Burns00b4b2d2019-10-17 22:09:27 -04005530 rowToRemove.getEntry().getKey(),
Ned Burns61269442019-05-02 18:27:23 -04005531 null /* ranking */,
5532 NotificationListenerService.REASON_CANCEL_ALL);
5533 } else {
5534 mEntryManager.performRemoveNotification(
Ned Burns00b4b2d2019-10-17 22:09:27 -04005535 rowToRemove.getEntry().getSbn(),
Ned Burns61269442019-05-02 18:27:23 -04005536 NotificationListenerService.REASON_CANCEL_ALL);
5537 }
Jason Monke59dc402018-08-16 12:05:01 -04005538 } else {
5539 rowToRemove.resetTranslation();
5540 }
5541 }
Ned Burns61269442019-05-02 18:27:23 -04005542 if (selection == ROWS_ALL) {
5543 try {
5544 mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
5545 } catch (Exception ex) {
5546 }
Jason Monke59dc402018-08-16 12:05:01 -04005547 }
5548 });
Jason Monke59dc402018-08-16 12:05:01 -04005549 }
5550
Ned Burns61269442019-05-02 18:27:23 -04005551 private boolean includeChildInDismissAll(
5552 ExpandableNotificationRow row,
5553 @SelectedRows int selection) {
Steve Elliott946b00c2019-10-28 11:20:24 -04005554 return canChildBeDismissed(row) && matchesSelection(row, selection);
Ned Burns61269442019-05-02 18:27:23 -04005555 }
5556
5557 /**
5558 * Given a list of rows, animates them away in a staggered fashion as if they were dismissed.
5559 * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete
5560 * handler.
5561 *
5562 * @param hideAnimatedList List of rows to animated away. Should only be views that are
5563 * currently visible, or else the stagger will look funky.
5564 * @param closeShade Whether to close the shade after the stagger animation completes.
5565 * @param onAnimationComplete Called after the entire animation completes (including the shade
5566 * closing if appropriate). The rows must be dismissed for real here.
5567 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005568 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -04005569 private void performDismissAllAnimations(
5570 final ArrayList<View> hideAnimatedList,
5571 final boolean closeShade,
5572 final Runnable onAnimationComplete) {
5573
5574 final Runnable onSlideAwayAnimationComplete = () -> {
5575 if (closeShade) {
Dave Mankoffaf8163f2020-01-08 14:24:35 -05005576 Dependency.get(ShadeController.class).addPostCollapseAction(() -> {
Ned Burns61269442019-05-02 18:27:23 -04005577 setDismissAllInProgress(false);
5578 onAnimationComplete.run();
5579 });
Dave Mankoffaf8163f2020-01-08 14:24:35 -05005580 Dependency.get(ShadeController.class).animateCollapsePanels(
5581 CommandQueue.FLAG_EXCLUDE_NONE);
Ned Burns61269442019-05-02 18:27:23 -04005582 } else {
5583 setDismissAllInProgress(false);
5584 onAnimationComplete.run();
5585 }
Jason Monke59dc402018-08-16 12:05:01 -04005586 };
5587
5588 if (hideAnimatedList.isEmpty()) {
Ned Burns61269442019-05-02 18:27:23 -04005589 onSlideAwayAnimationComplete.run();
Jason Monke59dc402018-08-16 12:05:01 -04005590 return;
5591 }
5592
5593 // let's disable our normal animations
5594 setDismissAllInProgress(true);
5595
5596 // Decrease the delay for every row we animate to give the sense of
5597 // accelerating the swipes
5598 int rowDelayDecrement = 10;
5599 int currentDelay = 140;
5600 int totalDelay = 180;
5601 int numItems = hideAnimatedList.size();
5602 for (int i = numItems - 1; i >= 0; i--) {
5603 View view = hideAnimatedList.get(i);
5604 Runnable endRunnable = null;
5605 if (i == 0) {
Ned Burns61269442019-05-02 18:27:23 -04005606 endRunnable = onSlideAwayAnimationComplete;
Jason Monke59dc402018-08-16 12:05:01 -04005607 }
Lucas Dupinfb8bdbb2018-12-02 15:09:37 -08005608 dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
Jason Monke59dc402018-08-16 12:05:01 -04005609 currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
5610 totalDelay += currentDelay;
5611 }
5612 }
5613
5614 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005615 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005616 protected void inflateFooterView() {
5617 FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
5618 R.layout.status_bar_notification_footer, this, false);
5619 footerView.setDismissButtonClickListener(v -> {
5620 mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
Ned Burns61269442019-05-02 18:27:23 -04005621 clearNotifications(ROWS_ALL, true /* closeShade */);
Jason Monke59dc402018-08-16 12:05:01 -04005622 });
5623 footerView.setManageButtonClickListener(this::manageNotifications);
5624 setFooterView(footerView);
5625 }
5626
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005627 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5628 private void inflateEmptyShadeView() {
Jason Monke59dc402018-08-16 12:05:01 -04005629 EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
5630 R.layout.status_bar_no_notifications, this, false);
5631 view.setText(R.string.empty_shade_text);
5632 setEmptyShadeView(view);
5633 }
5634
5635 /**
5636 * Updates expanded, dimmed and locked states of notification rows.
5637 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005638 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jason Monke59dc402018-08-16 12:05:01 -04005639 public void onUpdateRowStates() {
5640 changeViewPosition(mFooterView, -1);
5641
5642 // The following views will be moved to the end of mStackScroller. This counter represents
5643 // the offset from the last child. Initialized to 1 for the very last position. It is post-
5644 // incremented in the following "changeViewPosition" calls so that its value is correct for
5645 // subsequent calls.
5646 int offsetFromEnd = 1;
5647 changeViewPosition(mEmptyShadeView,
5648 getChildCount() - offsetFromEnd++);
5649
5650 // No post-increment for this call because it is the last one. Make sure to add one if
5651 // another "changeViewPosition" call is ever added.
5652 changeViewPosition(mShelf,
5653 getChildCount() - offsetFromEnd);
Jason Monke59dc402018-08-16 12:05:01 -04005654 }
5655
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005656 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffaf8163f2020-01-08 14:24:35 -05005657 public void setNotificationPanelController(
5658 NotificationPanelViewController notificationPanelViewController) {
5659 mNotificationPanelController = notificationPanelViewController;
Jason Monke59dc402018-08-16 12:05:01 -04005660 }
5661
Jason Monk297c04e2018-08-23 17:16:59 -04005662 public void updateIconAreaViews() {
5663 mIconAreaController.updateNotificationIcons();
5664 }
5665
Selim Cinek60ffea62018-03-22 13:16:44 -07005666 /**
Selim Cinek3d6ae232019-01-04 14:14:33 -08005667 * Set how far the wake up is when waking up from pulsing. This is a height and will adjust the
5668 * notification positions accordingly.
5669 * @param height the new wake up height
5670 * @return the overflow how much the height is further than he lowest notification
5671 */
Selim Cinek5040f2e2019-02-14 18:22:42 -08005672 public float setPulseHeight(float height) {
5673 mAmbientState.setPulseHeight(height);
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005674 if (mKeyguardBypassController.getBypassEnabled()) {
5675 notifyAppearChangedListeners();
5676 }
Selim Cinek3d6ae232019-01-04 14:14:33 -08005677 requestChildrenUpdate();
5678 return Math.max(0, height - mAmbientState.getInnerHeight(true /* ignorePulseHeight */));
5679 }
5680
Selim Cinekb0fada62019-06-17 19:03:59 -07005681 public float getPulseHeight() {
5682 return mAmbientState.getPulseHeight();
5683 }
5684
Selim Cinek3d6ae232019-01-04 14:14:33 -08005685 /**
Selim Cinek195dfc52019-05-30 19:35:05 -07005686 * Set the amount how much we're dozing. This is different from how hidden the shade is, when
Selim Cinek3d6ae232019-01-04 14:14:33 -08005687 * the notification is pulsing.
5688 */
5689 public void setDozeAmount(float dozeAmount) {
5690 mAmbientState.setDozeAmount(dozeAmount);
Selim Cinekae55d832019-02-22 17:43:43 -08005691 updateContinuousBackgroundDrawing();
Selim Cinek3d6ae232019-01-04 14:14:33 -08005692 requestChildrenUpdate();
5693 }
5694
Selim Cinek459aee32019-02-20 11:18:56 -08005695 public void wakeUpFromPulse() {
Selim Cinekb0fada62019-06-17 19:03:59 -07005696 setPulseHeight(getWakeUpHeight());
Selim Cinek459aee32019-02-20 11:18:56 -08005697 // Let's place the hidden views at the end of the pulsing notification to make sure we have
5698 // a smooth animation
5699 boolean firstVisibleView = true;
5700 float wakeUplocation = -1f;
5701 int childCount = getChildCount();
5702 for (int i = 0; i < childCount; i++) {
5703 ExpandableView view = (ExpandableView) getChildAt(i);
5704 if (view.getVisibility() == View.GONE) {
5705 continue;
5706 }
5707 boolean isShelf = view == mShelf;
5708 if (!(view instanceof ExpandableNotificationRow) && !isShelf) {
5709 continue;
5710 }
5711 if (view.getVisibility() == View.VISIBLE && !isShelf) {
5712 if (firstVisibleView) {
5713 firstVisibleView = false;
5714 wakeUplocation = view.getTranslationY()
5715 + view.getActualHeight() - mShelf.getIntrinsicHeight();
5716 }
5717 } else if (!firstVisibleView) {
5718 view.setTranslationY(wakeUplocation);
5719 }
5720 }
Selim Cinekf434a742019-05-28 17:39:49 -07005721 mDimmedNeedsAnimation = true;
Selim Cinek459aee32019-02-20 11:18:56 -08005722 }
5723
Selim Cinek6f0a62a2019-04-09 18:40:12 -07005724 @Override
5725 public void onDynamicPrivacyChanged() {
5726 if (mIsExpanded) {
5727 // The bottom might change because we're using the final actual height of the view
5728 mAnimateBottomOnLayout = true;
5729 }
Selim Cinekabcc2012019-07-23 18:44:07 -07005730 // Let's update the footer once the notifications have been updated (in the next frame)
Selim Cinek5454a0d2019-07-30 17:14:50 -07005731 post(() -> {
5732 updateFooter();
5733 updateSectionBoundaries();
5734 });
Selim Cinek6f0a62a2019-04-09 18:40:12 -07005735 }
5736
Selim Cinekb0fada62019-06-17 19:03:59 -07005737 public void setOnPulseHeightChangedListener(Runnable listener) {
5738 mAmbientState.setOnPulseHeightChangedListener(listener);
5739 }
5740
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005741 public float calculateAppearFractionBypass() {
5742 float pulseHeight = getPulseHeight();
5743 float wakeUpHeight = getWakeUpHeight();
5744 float dragDownAmount = pulseHeight - wakeUpHeight;
5745
5746 // The total distance required to fully reveal the header
5747 float totalDistance = getIntrinsicPadding();
5748 return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
5749 }
5750
Selim Cinek3d6ae232019-01-04 14:14:33 -08005751 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005752 * A listener that is notified when the empty space below the notifications is clicked on
5753 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005754 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005755 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08005756 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005757 }
5758
5759 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02005760 * A listener that gets notified when the overscroll at the top has changed.
5761 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005762 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi290600a2014-05-30 17:02:20 +02005763 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02005764
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005765 /**
5766 * Notifies a listener that the overscroll has changed.
5767 *
5768 * @param amount the amount of overscroll, in pixels
5769 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
5770 * unrubberbanded motion to directly expand overscroll view (e.g
5771 * expand
5772 * QS)
5773 */
5774 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02005775
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005776 /**
5777 * Notify a listener that the scroller wants to escape from the scrolling motion and
5778 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
5779 *
5780 * @param velocity The velocity that the Scroller had when over flinging
5781 * @param open Should the fling open or close the overscroll view.
5782 */
5783 void flingTopOverscroll(float velocity, boolean open);
5784 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02005785
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005786 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005787 public void updateSpeedBumpIndex() {
Jason Monke59dc402018-08-16 12:05:01 -04005788 int speedBumpIndex = 0;
5789 int currentIndex = 0;
5790 final int N = getChildCount();
5791 for (int i = 0; i < N; i++) {
5792 View view = getChildAt(i);
5793 if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
5794 continue;
5795 }
5796 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
5797 currentIndex++;
Gus Prevas33619af2018-10-26 15:40:27 -04005798 boolean beforeSpeedBump;
Selim Cinekda330ff2019-06-11 15:41:28 -07005799 if (mHighPriorityBeforeSpeedBump) {
Evan Laird25f02752019-08-14 19:25:06 -04005800 beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT;
Selim Cinekda330ff2019-06-11 15:41:28 -07005801 } else {
Ned Burns60e94592019-09-06 14:47:25 -04005802 beforeSpeedBump = !row.getEntry().isAmbient();
Gus Prevas33619af2018-10-26 15:40:27 -04005803 }
5804 if (beforeSpeedBump) {
Jason Monke59dc402018-08-16 12:05:01 -04005805 speedBumpIndex = currentIndex;
5806 }
5807 }
5808 boolean noAmbient = speedBumpIndex == N;
5809 updateSpeedBumpIndex(speedBumpIndex, noAmbient);
5810 }
5811
Gus Prevase2d6f042018-10-17 15:25:30 -04005812 /** Updates the indices of the boundaries between sections. */
5813 @ShadeViewRefactor(RefactorComponent.INPUT)
5814 public void updateSectionBoundaries() {
Ned Burns9eb06332019-04-23 16:02:12 -04005815 mSectionsManager.updateSectionBoundaries();
Gus Prevase2d6f042018-10-17 15:25:30 -04005816 }
5817
Selim Cinekae55d832019-02-22 17:43:43 -08005818 private void updateContinuousBackgroundDrawing() {
5819 boolean continuousBackground = !mAmbientState.isFullyAwake()
5820 && !mAmbientState.getDraggedViews().isEmpty();
5821 if (continuousBackground != mContinuousBackgroundUpdate) {
5822 mContinuousBackgroundUpdate = continuousBackground;
5823 if (continuousBackground) {
5824 getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater);
5825 } else {
5826 getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater);
5827 }
5828 }
5829 }
5830
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005831 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08005832 private void updateContinuousShadowDrawing() {
5833 boolean continuousShadowUpdate = mAnimationRunning
5834 || !mAmbientState.getDraggedViews().isEmpty();
5835 if (continuousShadowUpdate != mContinuousShadowUpdate) {
5836 if (continuousShadowUpdate) {
5837 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
5838 } else {
5839 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
5840 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07005841 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08005842 }
5843 }
5844
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005845 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005846 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -08005847 public void resetExposedMenuView(boolean animate, boolean force) {
5848 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07005849 }
5850
Ned Burns61269442019-05-02 18:27:23 -04005851 private static boolean matchesSelection(
5852 ExpandableNotificationRow row,
5853 @SelectedRows int selection) {
5854 switch (selection) {
5855 case ROWS_ALL:
5856 return true;
5857 case ROWS_HIGH_PRIORITY:
Evan Laird25f02752019-08-14 19:25:06 -04005858 return row.getEntry().getBucket() < BUCKET_SILENT;
Ned Burns61269442019-05-02 18:27:23 -04005859 case ROWS_GENTLE:
Evan Laird25f02752019-08-14 19:25:06 -04005860 return row.getEntry().getBucket() == BUCKET_SILENT;
Ned Burns61269442019-05-02 18:27:23 -04005861 default:
5862 throw new IllegalArgumentException("Unknown selection: " + selection);
5863 }
5864 }
5865
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005866 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
5867 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02005868
Jason Monke59dc402018-08-16 12:05:01 -04005869 static AnimationFilter[] FILTERS = new AnimationFilter[]{
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005870
5871 // ANIMATION_TYPE_ADD
5872 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005873 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005874 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005875 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005876 .animateZ()
5877 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005878
5879 // ANIMATION_TYPE_REMOVE
5880 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005881 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005882 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005883 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005884 .animateZ()
5885 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005886
5887 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5888 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005889 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005890 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005891 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005892 .animateZ()
5893 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005894
5895 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5896 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005897 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005898 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005899 .animateY()
5900 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005901 .animateZ(),
5902
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005903 // ANIMATION_TYPE_ACTIVATED_CHILD
5904 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005905 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005906
5907 // ANIMATION_TYPE_DIMMED
5908 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005909 .animateDimmed(),
5910
5911 // ANIMATION_TYPE_CHANGE_POSITION
5912 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005913 .animateAlpha() // maybe the children change positions
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005914 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005915 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005916 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04005917 .animateZ(),
5918
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005919 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5920 new AnimationFilter()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005921 .animateHeight()
5922 .animateTopInset()
5923 .animateY()
5924 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02005925 .animateZ()
5926 .hasDelays(),
5927
5928 // ANIMATION_TYPE_HIDE_SENSITIVE
5929 new AnimationFilter()
5930 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02005931
5932 // ANIMATION_TYPE_VIEW_RESIZE
5933 new AnimationFilter()
Selim Cineka5e211b2014-08-11 17:35:48 +02005934 .animateHeight()
5935 .animateTopInset()
5936 .animateY()
5937 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02005938
Selim Cinekb5605e52015-02-20 18:21:41 +01005939 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
5940 new AnimationFilter()
5941 .animateAlpha()
5942 .animateHeight()
5943 .animateTopInset()
5944 .animateY()
5945 .animateZ(),
5946
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005947 // ANIMATION_TYPE_HEADS_UP_APPEAR
5948 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005949 .animateHeight()
5950 .animateTopInset()
5951 .animateY()
5952 .animateZ(),
5953
5954 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5955 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005956 .animateHeight()
5957 .animateTopInset()
5958 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07005959 .animateZ()
5960 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005961
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005962 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5963 new AnimationFilter()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005964 .animateHeight()
5965 .animateTopInset()
5966 .animateY()
5967 .animateZ()
5968 .hasDelays(),
5969
Selim Cineka59ecc32015-04-07 10:51:49 -07005970 // ANIMATION_TYPE_HEADS_UP_OTHER
5971 new AnimationFilter()
Selim Cineka59ecc32015-04-07 10:51:49 -07005972 .animateHeight()
5973 .animateTopInset()
5974 .animateY()
5975 .animateZ(),
5976
Selim Cinekd9acca52014-09-01 22:33:25 +02005977 // ANIMATION_TYPE_EVERYTHING
5978 new AnimationFilter()
5979 .animateAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02005980 .animateDimmed()
5981 .animateHideSensitive()
5982 .animateHeight()
5983 .animateTopInset()
5984 .animateY()
5985 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005986 };
5987
Jason Monke59dc402018-08-16 12:05:01 -04005988 static int[] LENGTHS = new int[]{
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005989
5990 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005991 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005992
5993 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005994 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005995
5996 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5997 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5998
5999 // ANIMATION_TYPE_TOP_PADDING_CHANGED
6000 StackStateAnimator.ANIMATION_DURATION_STANDARD,
6001
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006002 // ANIMATION_TYPE_ACTIVATED_CHILD
6003 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
6004
6005 // ANIMATION_TYPE_DIMMED
6006 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006007
6008 // ANIMATION_TYPE_CHANGE_POSITION
6009 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04006010
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006011 // ANIMATION_TYPE_GO_TO_FULL_SHADE
6012 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02006013
6014 // ANIMATION_TYPE_HIDE_SENSITIVE
6015 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02006016
6017 // ANIMATION_TYPE_VIEW_RESIZE
6018 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02006019
Selim Cinekb5605e52015-02-20 18:21:41 +01006020 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08006021 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01006022
Selim Cinekb8f09cf2015-03-16 17:09:28 -07006023 // ANIMATION_TYPE_HEADS_UP_APPEAR
6024 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
6025
6026 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
6027 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
6028
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07006029 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
6030 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
6031
Selim Cineka59ecc32015-04-07 10:51:49 -07006032 // ANIMATION_TYPE_HEADS_UP_OTHER
6033 StackStateAnimator.ANIMATION_DURATION_STANDARD,
6034
Selim Cinekd9acca52014-09-01 22:33:25 +02006035 // ANIMATION_TYPE_EVERYTHING
6036 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006037 };
6038
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006039 static final int ANIMATION_TYPE_ADD = 0;
6040 static final int ANIMATION_TYPE_REMOVE = 1;
6041 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
6042 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
Selim Cinekff2ffec2018-11-19 18:52:01 -08006043 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 4;
6044 static final int ANIMATION_TYPE_DIMMED = 5;
6045 static final int ANIMATION_TYPE_CHANGE_POSITION = 6;
Selim Cinekc3fec682019-06-06 18:11:07 -07006046 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 7;
6047 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 8;
6048 static final int ANIMATION_TYPE_VIEW_RESIZE = 9;
6049 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 10;
6050 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 11;
6051 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 12;
6052 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 13;
6053 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 14;
6054 static final int ANIMATION_TYPE_EVERYTHING = 15;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02006055
Selim Cinek572bbd42014-04-25 16:43:27 +02006056 final long eventStartTime;
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006057 final ExpandableView mChangingView;
Selim Cinek572bbd42014-04-25 16:43:27 +02006058 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02006059 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006060 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006061 View viewAfterChangingView;
Selim Cineka59ecc32015-04-07 10:51:49 -07006062 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02006063
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006064 AnimationEvent(ExpandableView view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006065 this(view, type, LENGTHS[type]);
6066 }
6067
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006068 AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006069 this(view, type, LENGTHS[type], filter);
6070 }
6071
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006072 AnimationEvent(ExpandableView view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006073 this(view, type, length, FILTERS[type]);
6074 }
6075
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006076 AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02006077 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006078 mChangingView = view;
Selim Cinek572bbd42014-04-25 16:43:27 +02006079 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006080 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07006081 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006082 }
6083
6084 /**
6085 * Combines the length of several animation events into a single value.
6086 *
6087 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006088 * @return The combined length. Depending on the event types, this might be the maximum of
Jason Monke59dc402018-08-16 12:05:01 -04006089 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006090 */
6091 static long combineLength(ArrayList<AnimationEvent> events) {
6092 long length = 0;
6093 int size = events.size();
6094 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006095 AnimationEvent event = events.get(i);
6096 length = Math.max(length, event.length);
6097 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
6098 return event.length;
6099 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006100 }
6101 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02006102 }
6103 }
Jason Monke59dc402018-08-16 12:05:01 -04006104
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006105 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
6106 private final StateListener mStateListener = new StateListener() {
Jason Monke59dc402018-08-16 12:05:01 -04006107 @Override
6108 public void onStatePreChange(int oldState, int newState) {
6109 if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
6110 requestAnimateEverything();
6111 }
6112 }
6113
6114 @Override
6115 public void onStateChanged(int newState) {
6116 setStatusBarState(newState);
6117 }
Evan Laird91d0f102018-09-18 17:39:55 -04006118
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006119 @Override
6120 public void onStatePostChange() {
Evan Laird91d0f102018-09-18 17:39:55 -04006121 NotificationStackScrollLayout.this.onStatePostChange();
6122 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006123 };
6124
Will Brockmane718d582019-01-17 16:38:38 -05006125 @VisibleForTesting
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006126 @ShadeViewRefactor(RefactorComponent.INPUT)
Will Brockmane718d582019-01-17 16:38:38 -05006127 protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006128 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006129 public void onMenuClicked(View view, int x, int y, MenuItem item) {
6130 if (mLongPressListener == null) {
6131 return;
6132 }
6133 if (view instanceof ExpandableNotificationRow) {
6134 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Ned Burns1c2b85a42019-11-14 15:37:03 -05006135 mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
Will Brockmane718d582019-01-17 16:38:38 -05006136 .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
6137 .setType(MetricsEvent.TYPE_ACTION)
6138 );
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006139 }
6140 mLongPressListener.onLongPress(view, x, y, item);
6141 }
6142
6143 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006144 public void onMenuReset(View row) {
6145 View translatingParentView = mSwipeHelper.getTranslatingParentView();
6146 if (translatingParentView != null && row == translatingParentView) {
6147 mSwipeHelper.clearExposedMenuView();
6148 mSwipeHelper.clearTranslatingParentView();
Gus Prevas211181532018-12-13 14:49:33 -05006149 if (row instanceof ExpandableNotificationRow) {
6150 mHeadsUpManager.setMenuShown(
6151 ((ExpandableNotificationRow) row).getEntry(), false);
6152
6153 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006154 }
6155 }
6156
6157 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006158 public void onMenuShown(View row) {
6159 if (row instanceof ExpandableNotificationRow) {
Gus Prevas211181532018-12-13 14:49:33 -05006160 ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
Ned Burns1c2b85a42019-11-14 15:37:03 -05006161 mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
Will Brockmane718d582019-01-17 16:38:38 -05006162 .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
6163 .setType(MetricsEvent.TYPE_ACTION));
Gus Prevas211181532018-12-13 14:49:33 -05006164 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
Evan Lairde55c6012019-03-13 12:54:37 -04006165 mSwipeHelper.onMenuShown(row);
Steve Elliottff6c25e2019-05-30 17:38:52 -04006166 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
6167 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
6168 false /* resetMenu */);
Evan Lairde55c6012019-03-13 12:54:37 -04006169
6170 // Check to see if we want to go directly to the notfication guts
6171 NotificationMenuRowPlugin provider = notificationRow.getProvider();
6172 if (provider.shouldShowGutsOnSnapOpen()) {
6173 MenuItem item = provider.menuItemToExposeOnSnap();
6174 if (item != null) {
6175 Point origin = provider.getRevealAnimationOrigin();
Steve Elliottff6c25e2019-05-30 17:38:52 -04006176 mNotificationGutsManager.openGuts(row, origin.x, origin.y, item);
Evan Lairde55c6012019-03-13 12:54:37 -04006177 } else {
6178 Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
6179 + "menu item in menuItemtoExposeOnSnap. Skipping.");
6180 }
6181
6182 // Close the menu row since we went directly to the guts
6183 resetExposedMenuView(false, true);
6184 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006185 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006186 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006187 };
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006188
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006189 @ShadeViewRefactor(RefactorComponent.INPUT)
6190 private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
6191 new NotificationSwipeHelper.NotificationCallback() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006192 @Override
6193 public void onDismiss() {
6194 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
6195 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
6196 false /* resetMenu */);
6197 }
6198
6199 @Override
6200 public void onSnooze(StatusBarNotification sbn,
6201 NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
6202 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
6203 }
6204
6205 @Override
Julia Reynolds12ba4cf2020-01-10 16:01:38 -05006206 public void onSnooze(StatusBarNotification sbn, int hours) {
6207 mStatusBar.setNotificationSnoozed(sbn, hours);
6208 }
6209
6210 @Override
Selim Cinekae55d832019-02-22 17:43:43 -08006211 public boolean shouldDismissQuickly() {
6212 return NotificationStackScrollLayout.this.isExpanded() && mAmbientState.isFullyAwake();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006213 }
6214
6215 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006216 public void onDragCancelled(View v) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006217 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006218 mFalsingManager.onNotificatonStopDismissing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006219 }
6220
6221 /**
6222 * Handles cleanup after the given {@code view} has been fully swiped out (including
6223 * re-invoking dismiss logic in case the notification has not made its way out yet).
6224 */
6225 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006226 public void onChildDismissed(View view) {
Steve Elliott946b00c2019-10-28 11:20:24 -04006227 if (!(view instanceof ActivatableNotificationView)) {
6228 return;
6229 }
6230 ActivatableNotificationView row = (ActivatableNotificationView) view;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006231 if (!row.isDismissed()) {
6232 handleChildViewDismissed(view);
6233 }
6234 ViewGroup transientContainer = row.getTransientContainer();
6235 if (transientContainer != null) {
6236 transientContainer.removeTransientView(view);
6237 }
6238 }
6239
6240 /**
6241 * Starts up notification dismiss and tells the notification, if any, to remove itself from
6242 * layout.
6243 *
6244 * @param view view (e.g. notification) to dismiss from the layout
6245 */
6246
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006247 public void handleChildViewDismissed(View view) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006248 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006249 if (mDismissAllInProgress) {
6250 return;
6251 }
6252
6253 boolean isBlockingHelperShown = false;
6254
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006255 mAmbientState.onDragFinished(view);
6256 updateContinuousShadowDrawing();
6257
6258 if (view instanceof ExpandableNotificationRow) {
6259 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6260 if (row.isHeadsUp()) {
6261 mHeadsUpManager.addSwipedOutNotification(
Ned Burns1c2b85a42019-11-14 15:37:03 -05006262 row.getEntry().getSbn().getKey());
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006263 }
6264 isBlockingHelperShown =
6265 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
6266 }
6267
Steve Elliott946b00c2019-10-28 11:20:24 -04006268 if (view instanceof PeopleHubView) {
6269 PeopleHubView row = (PeopleHubView) view;
6270 row.dismiss(false);
6271 mSectionsManager.hidePeopleRow();
6272 }
6273
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006274 if (!isBlockingHelperShown) {
6275 mSwipedOutViews.add(view);
6276 }
6277 mFalsingManager.onNotificationDismissed();
6278 if (mFalsingManager.shouldEnforceBouncer()) {
6279 mStatusBar.executeRunnableDismissingKeyguard(
6280 null,
6281 null /* cancelAction */,
6282 false /* dismissShade */,
6283 true /* afterKeyguardGone */,
6284 false /* deferred */);
6285 }
6286 }
6287
6288 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006289 public boolean isAntiFalsingNeeded() {
6290 return onKeyguard();
6291 }
6292
6293 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006294 public View getChildAtPosition(MotionEvent ev) {
6295 View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
6296 ev.getY());
6297 if (child instanceof ExpandableNotificationRow) {
6298 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
6299 ExpandableNotificationRow parent = row.getNotificationParent();
6300 if (parent != null && parent.areChildrenExpanded()
6301 && (parent.areGutsExposed()
6302 || mSwipeHelper.getExposedMenuView() == parent
6303 || (parent.getNotificationChildren().size() == 1
Evan Laird94492852018-10-25 13:43:01 -04006304 && parent.getEntry().isClearable()))) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006305 // In this case the group is expanded and showing the menu for the
6306 // group, further interaction should apply to the group, not any
6307 // child notifications so we use the parent of the child. We also do the same
6308 // if we only have a single child.
6309 child = parent;
6310 }
6311 }
6312 return child;
6313 }
6314
6315 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006316 public void onBeginDrag(View v) {
6317 mFalsingManager.onNotificatonStartDismissing();
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006318 setSwipingInProgress(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006319 mAmbientState.onBeginDrag((ExpandableView) v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006320 updateContinuousShadowDrawing();
Selim Cinekae55d832019-02-22 17:43:43 -08006321 updateContinuousBackgroundDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006322 requestChildrenUpdate();
6323 }
6324
6325 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006326 public void onChildSnappedBack(View animView, float targetLeft) {
6327 mAmbientState.onDragFinished(animView);
6328 updateContinuousShadowDrawing();
Selim Cinekae55d832019-02-22 17:43:43 -08006329 updateContinuousBackgroundDrawing();
Selim Cineke3c6e462019-06-24 19:37:06 -07006330 if (animView instanceof ExpandableNotificationRow) {
6331 ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
6332 if (row.isPinned() && !canChildBeDismissed(row)
Ned Burns1c2b85a42019-11-14 15:37:03 -05006333 && row.getEntry().getSbn().getNotification().fullScreenIntent
Selim Cineke3c6e462019-06-24 19:37:06 -07006334 == null) {
Ned Burns1c2b85a42019-11-14 15:37:03 -05006335 mHeadsUpManager.removeNotification(row.getEntry().getSbn().getKey(),
Selim Cineke3c6e462019-06-24 19:37:06 -07006336 true /* removeImmediately */);
6337 }
6338 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006339 }
6340
6341 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006342 public boolean updateSwipeProgress(View animView, boolean dismissable,
6343 float swipeProgress) {
6344 // Returning true prevents alpha fading.
6345 return !mFadeNotificationsOnDismiss;
6346 }
6347
6348 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006349 public float getFalsingThresholdFactor() {
6350 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
6351 }
6352
6353 @Override
Gus Prevasc4e68d42019-01-17 15:45:21 -05006354 public int getConstrainSwipeStartPosition() {
6355 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
6356 if (menuRow != null) {
6357 return Math.abs(menuRow.getMenuSnapTarget());
6358 }
6359 return 0;
6360 }
6361
Steve Elliott946b00c2019-10-28 11:20:24 -04006362 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006363 public boolean canChildBeDismissed(View v) {
Steve Elliott946b00c2019-10-28 11:20:24 -04006364 return NotificationStackScrollLayout.canChildBeDismissed(v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006365 }
Gus Prevas37d67e22018-11-02 14:48:55 -04006366
6367 @Override
6368 public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
Evan Laird30b9b162019-04-24 15:22:24 -04006369 //TODO: b/131242807 for why this doesn't do anything with direction
6370 return canChildBeDismissed(v);
Gus Prevas37d67e22018-11-02 14:48:55 -04006371 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006372 };
6373
Steve Elliott946b00c2019-10-28 11:20:24 -04006374 private static boolean canChildBeDismissed(View v) {
6375 if (v instanceof ExpandableNotificationRow) {
6376 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
6377 if (row.isBlockingHelperShowingAndTranslationFinished()) {
6378 return true;
6379 }
6380 if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
6381 return false;
6382 }
6383 return row.canViewBeDismissed();
6384 }
6385 if (v instanceof PeopleHubView) {
6386 return true;
6387 }
6388 return false;
6389 }
6390
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006391 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
6392
6393 @ShadeViewRefactor(RefactorComponent.INPUT)
6394 private final DragDownCallback mDragDownCallback = new DragDownCallback() {
6395
6396 /* Only ever called as a consequence of a lockscreen expansion gesture. */
6397 @Override
6398 public boolean onDraggedDown(View startingChild, int dragLengthY) {
6399 if (mStatusBarState == StatusBarState.KEYGUARD
Evan Laird181de622019-10-24 09:53:02 -04006400 && mEntryManager.hasActiveNotifications()) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006401 mLockscreenGestureLogger.write(
6402 MetricsEvent.ACTION_LS_SHADE,
6403 (int) (dragLengthY / mDisplayMetrics.density),
6404 0 /* velocityDp - N/A */);
6405
Selim Cinekc1d9ab22019-05-21 18:08:30 -07006406 if (!mAmbientState.isDozing() || startingChild != null) {
Lucas Dupin55c6e802018-09-27 18:07:36 -07006407 // We have notifications, go to locked shade.
Dave Mankoffaf8163f2020-01-08 14:24:35 -05006408 Dependency.get(ShadeController.class).goToLockedShade(startingChild);
Lucas Dupin55c6e802018-09-27 18:07:36 -07006409 if (startingChild instanceof ExpandableNotificationRow) {
6410 ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
6411 row.onExpandedByGesture(true /* drag down is always an open */);
6412 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006413 }
Lucas Dupin55c6e802018-09-27 18:07:36 -07006414
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006415 return true;
Selim Cinek5454a0d2019-07-30 17:14:50 -07006416 } else if (mDynamicPrivacyController.isInLockedDownShade()) {
6417 mStatusbarStateController.setLeaveOpenOnKeyguardHide(true);
6418 mStatusBar.dismissKeyguardThenExecute(() -> false /* dismissAction */,
6419 null /* cancelRunnable */, false /* afterKeyguardGone */);
6420 return true;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006421 } else {
6422 // abort gesture.
6423 return false;
6424 }
6425 }
6426
6427 @Override
6428 public void onDragDownReset() {
6429 setDimmed(true /* dimmed */, true /* animated */);
6430 resetScrollPosition();
6431 resetCheckSnoozeLeavebehind();
6432 }
6433
6434 @Override
6435 public void onCrossedThreshold(boolean above) {
6436 setDimmed(!above /* dimmed */, true /* animate */);
6437 }
6438
6439 @Override
6440 public void onTouchSlopExceeded() {
6441 cancelLongPress();
6442 checkSnoozeLeavebehind();
6443 }
6444
6445 @Override
6446 public void setEmptyDragAmount(float amount) {
Dave Mankoffaf8163f2020-01-08 14:24:35 -05006447 mNotificationPanelController.setEmptyDragAmount(amount);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006448 }
6449
6450 @Override
6451 public boolean isFalsingCheckNeeded() {
6452 return mStatusBarState == StatusBarState.KEYGUARD;
6453 }
Selim Cinek5454a0d2019-07-30 17:14:50 -07006454
6455 @Override
6456 public boolean isDragDownEnabledForView(ExpandableView view) {
6457 if (isDragDownAnywhereEnabled()) {
6458 return true;
6459 }
6460 if (mDynamicPrivacyController.isInLockedDownShade()) {
6461 if (view == null) {
6462 // Dragging down is allowed in general
6463 return true;
6464 }
6465 if (view instanceof ExpandableNotificationRow) {
6466 // Only drag down on sensitive views, otherwise the ExpandHelper will take this
6467 return ((ExpandableNotificationRow) view).getEntry().isSensitive();
6468 }
6469 }
6470 return false;
6471 }
6472
6473 @Override
6474 public boolean isDragDownAnywhereEnabled() {
6475 return mStatusbarStateController.getState() == StatusBarState.KEYGUARD
6476 && !mKeyguardBypassController.getBypassEnabled();
6477 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006478 };
6479
6480 public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
6481
6482 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6483 private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
6484 @Override
6485 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6486 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6487 }
6488
6489 @Override
6490 public boolean isExpanded() {
6491 return mIsExpanded;
6492 }
6493
6494 @Override
6495 public Context getContext() {
6496 return mContext;
6497 }
6498 };
6499
6500 public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
6501
6502
6503 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6504 private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
6505 @Override
6506 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
6507 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
6508 && (mIsExpanded || changedRow.isPinned());
6509 if (animated) {
6510 mExpandedGroupView = changedRow;
6511 mNeedsAnimation = true;
6512 }
6513 changedRow.setChildrenExpanded(expanded, animated);
6514 if (!mGroupExpandedForMeasure) {
6515 onHeightChanged(changedRow, false /* needsAnimation */);
6516 }
6517 runAfterAnimationFinished(new Runnable() {
6518 @Override
6519 public void run() {
6520 changedRow.onFinishedExpansionChange();
6521 }
6522 });
6523 }
6524
6525 @Override
6526 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
Beverly85d4c192019-09-30 11:40:39 -04006527 mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren");
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006528 }
6529
6530 @Override
6531 public void onGroupsChanged() {
Beverly85d4c192019-09-30 11:40:39 -04006532 mStatusBar.requestNotificationUpdate("onGroupsChanged");
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006533 }
6534 };
6535
6536 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6537 private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
6538 @Override
6539 public ExpandableView getChildAtPosition(float touchX, float touchY) {
6540 return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY);
6541 }
6542
6543 @Override
6544 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6545 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6546 }
6547
6548 @Override
6549 public boolean canChildBeExpanded(View v) {
6550 return v instanceof ExpandableNotificationRow
6551 && ((ExpandableNotificationRow) v).isExpandable()
6552 && !((ExpandableNotificationRow) v).areGutsExposed()
6553 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
6554 }
6555
6556 /* Only ever called as a consequence of an expansion gesture in the shade. */
6557 @Override
6558 public void setUserExpandedChild(View v, boolean userExpanded) {
6559 if (v instanceof ExpandableNotificationRow) {
6560 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
6561 if (userExpanded && onKeyguard()) {
6562 // Due to a race when locking the screen while touching, a notification may be
6563 // expanded even after we went back to keyguard. An example of this happens if
6564 // you click in the empty space while expanding a group.
6565
6566 // We also need to un-user lock it here, since otherwise the content height
6567 // calculated might be wrong. We also can't invert the two calls since
6568 // un-userlocking it will trigger a layout switch in the content view.
6569 row.setUserLocked(false);
6570 updateContentHeight();
6571 notifyHeightChangeListener(row);
6572 return;
6573 }
6574 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
6575 row.onExpandedByGesture(userExpanded);
6576 }
6577 }
6578
6579 @Override
6580 public void setExpansionCancelled(View v) {
6581 if (v instanceof ExpandableNotificationRow) {
6582 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
6583 }
6584 }
6585
6586 @Override
6587 public void setUserLockedChild(View v, boolean userLocked) {
6588 if (v instanceof ExpandableNotificationRow) {
6589 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
6590 }
6591 cancelLongPress();
6592 requestDisallowInterceptTouchEvent(true);
6593 }
6594
6595 @Override
6596 public void expansionStateChanged(boolean isExpanding) {
6597 mExpandingNotification = isExpanding;
6598 if (!mExpandedInThisMotion) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05006599 if (ANCHOR_SCROLLING) {
6600 // TODO
6601 } else {
6602 mMaxScrollAfterExpand = mOwnScrollY;
6603 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006604 mExpandedInThisMotion = true;
6605 }
6606 }
6607
6608 @Override
6609 public int getMaxExpandHeight(ExpandableView view) {
6610 return view.getMaxContentHeight();
6611 }
6612 };
6613
6614 public ExpandHelper.Callback getExpandHelperCallback() {
6615 return mExpandHelperCallback;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006616 }
Ned Burns61269442019-05-02 18:27:23 -04006617
6618 /** Enum for selecting some or all notification rows (does not included non-notif views). */
6619 @Retention(SOURCE)
6620 @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
6621 public @interface SelectedRows {}
6622 /** All rows representing notifs. */
6623 public static final int ROWS_ALL = 0;
6624 /** Only rows where entry.isHighPriority() is true. */
6625 public static final int ROWS_HIGH_PRIORITY = 1;
6626 /** Only rows where entry.isHighPriority() is false. */
6627 public static final int ROWS_GENTLE = 2;
Selim Cinek67b22602014-03-10 15:40:16 +01006628}