blob: e2cb8d4517ff6b0107652964bbd9e6f5677edce2 [file] [log] [blame]
Selim Cinek67b22602014-03-10 15:40:16 +01001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.stack;
Selim Cinek67b22602014-03-10 15:40:16 +010018
Gus Prevas59ec2ff2018-12-28 16:20:28 -050019import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
Gus Prevase83700cb2018-12-14 11:42:51 -050020import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
Gus Prevas0fa58d62019-01-11 13:58:40 -050021import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
Gus Prevase83700cb2018-12-14 11:42:51 -050022import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
Selim Cinekda330ff2019-06-11 15:41:28 -070023import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
Gus Prevas59ec2ff2018-12-28 16:20:28 -050024import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
Selim Cinek2627d722018-01-19 12:16:49 -080025
Ned Burns61269442019-05-02 18:27:23 -040026import static java.lang.annotation.RetentionPolicy.SOURCE;
27
Selim Cinek614576e2016-01-20 10:54:09 -080028import android.animation.Animator;
29import android.animation.AnimatorListenerAdapter;
Selim Cinekd35c2792016-01-21 13:20:57 -080030import android.animation.TimeAnimator;
31import android.animation.ValueAnimator;
Ned Burns61269442019-05-02 18:27:23 -040032import android.annotation.IntDef;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040033import android.annotation.NonNull;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010034import android.annotation.Nullable;
Selim Cinek67b22602014-03-10 15:40:16 +010035import android.content.Context;
Jason Monke59dc402018-08-16 12:05:01 -040036import android.content.Intent;
Selim Cinek67b22602014-03-10 15:40:16 +010037import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080038import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010039import android.graphics.Canvas;
Lucas Dupind285cf02018-01-18 09:18:23 -080040import android.graphics.Color;
Lucas Dupin64e2f572019-03-21 14:21:14 -070041import android.graphics.Outline;
Selim Cinek67b22602014-03-10 15:40:16 +010042import android.graphics.Paint;
Evan Lairde55c6012019-03-13 12:54:37 -040043import android.graphics.Point;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010044import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080045import android.graphics.PorterDuff;
46import android.graphics.PorterDuffXfermode;
47import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070048import android.os.Bundle;
Jason Monke59dc402018-08-16 12:05:01 -040049import android.os.ServiceManager;
50import android.provider.Settings;
Mady Mellorc2ff0112019-03-28 14:18:06 -070051import android.service.notification.NotificationListenerService;
Mady Mellor95d743c2017-01-10 12:05:27 -080052import android.service.notification.StatusBarNotification;
Selim Cinek67b22602014-03-10 15:40:16 +010053import android.util.AttributeSet;
Jason Monke59dc402018-08-16 12:05:01 -040054import android.util.DisplayMetrics;
Selim Cinek67b22602014-03-10 15:40:16 +010055import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080056import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070057import android.util.Pair;
Lucas Dupine17ce522017-07-17 15:45:06 -070058import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070059import android.view.InputDevice;
Jason Monke59dc402018-08-16 12:05:01 -040060import android.view.LayoutInflater;
Selim Cinek67b22602014-03-10 15:40:16 +010061import android.view.MotionEvent;
62import android.view.VelocityTracker;
63import android.view.View;
64import android.view.ViewConfiguration;
65import android.view.ViewGroup;
Lucas Dupin64e2f572019-03-21 14:21:14 -070066import android.view.ViewOutlineProvider;
Selim Cinek343e6e22014-04-11 21:23:30 +020067import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070068import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070069import android.view.accessibility.AccessibilityEvent;
70import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020071import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080072import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010073import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070074import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070075
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040076import com.android.internal.annotations.VisibleForTesting;
77import com.android.internal.graphics.ColorUtils;
Mady Mellora41587b2016-02-11 18:43:06 -080078import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010079import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monke59dc402018-08-16 12:05:01 -040080import com.android.internal.statusbar.IStatusBarService;
Lucas Dupin4e023812018-04-02 21:19:23 -070081import com.android.keyguard.KeyguardSliceView;
Lucas Dupine17ce522017-07-17 15:45:06 -070082import com.android.settingslib.Utils;
Rohan Shah524cf7b2018-03-15 14:40:02 -070083import com.android.systemui.Dependency;
Jason Monke59dc402018-08-16 12:05:01 -040084import com.android.systemui.Dumpable;
Selim Cinek67b22602014-03-10 15:40:16 +010085import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080086import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010087import com.android.systemui.R;
88import com.android.systemui.SwipeHelper;
Dave Mankoffdde5ee62019-05-02 17:36:11 -040089import com.android.systemui.classifier.FalsingManagerFactory;
Jason Monke59dc402018-08-16 12:05:01 -040090import com.android.systemui.colorextraction.SysuiColorExtractor;
Ned Burns9eb06332019-04-23 16:02:12 -040091import com.android.systemui.plugins.ActivityStarter;
Dave Mankoff468d4f62019-05-08 14:56:29 -040092import com.android.systemui.plugins.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080093import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
94import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040095import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
Mady Mellor95d743c2017-01-10 12:05:27 -080096import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Beverly8fdb5332019-02-04 14:29:49 -050097import com.android.systemui.plugins.statusbar.StatusBarStateController;
98import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Jason Monke59dc402018-08-16 12:05:01 -040099import com.android.systemui.statusbar.CommandQueue;
100import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
Jorim Jaggia2052ea2014-08-05 16:22:30 +0200101import com.android.systemui.statusbar.EmptyShadeView;
Jason Monk297c04e2018-08-23 17:16:59 -0400102import com.android.systemui.statusbar.NotificationLockscreenUserManager;
103import com.android.systemui.statusbar.NotificationRemoteInputManager;
104import com.android.systemui.statusbar.NotificationShelf;
105import com.android.systemui.statusbar.RemoteInputController;
106import com.android.systemui.statusbar.StatusBarState;
Beverly8fdb5332019-02-04 14:29:49 -0500107import com.android.systemui.statusbar.SysuiStatusBarStateController;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700108import com.android.systemui.statusbar.notification.DynamicPrivacyController;
Jason Monk297c04e2018-08-23 17:16:59 -0400109import com.android.systemui.statusbar.notification.FakeShadowView;
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500110import com.android.systemui.statusbar.notification.NotificationEntryListener;
Jason Monk297c04e2018-08-23 17:16:59 -0400111import com.android.systemui.statusbar.notification.NotificationEntryManager;
112import com.android.systemui.statusbar.notification.NotificationUtils;
113import com.android.systemui.statusbar.notification.ShadeViewRefactor;
114import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
Jason Monk297c04e2018-08-23 17:16:59 -0400115import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -0500116import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Jason Monk297c04e2018-08-23 17:16:59 -0400117import com.android.systemui.statusbar.notification.logging.NotificationLogger;
118import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
Rohan Shah20790b82018-07-02 17:21:04 -0700119import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
120import com.android.systemui.statusbar.notification.row.ExpandableView;
121import com.android.systemui.statusbar.notification.row.FooterView;
122import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
Rohan Shah20790b82018-07-02 17:21:04 -0700123import com.android.systemui.statusbar.notification.row.NotificationGuts;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400124import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
Rohan Shah20790b82018-07-02 17:21:04 -0700125import com.android.systemui.statusbar.notification.row.NotificationSnooze;
126import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700127import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900128import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400129import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
Selim Cinekc3fec682019-06-06 18:11:07 -0700130import com.android.systemui.statusbar.phone.KeyguardBypassController;
Jason Monke59dc402018-08-16 12:05:01 -0400131import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
Selim Cinekb5605e52015-02-20 18:21:41 +0100132import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monke59dc402018-08-16 12:05:01 -0400133import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700134import com.android.systemui.statusbar.phone.NotificationIconAreaController;
Jason Monke59dc402018-08-16 12:05:01 -0400135import com.android.systemui.statusbar.phone.NotificationPanelView;
Selim Cinekaac93252015-04-14 20:04:12 -0700136import com.android.systemui.statusbar.phone.ScrimController;
Jason Monk297c04e2018-08-23 17:16:59 -0400137import com.android.systemui.statusbar.phone.ShadeController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800138import com.android.systemui.statusbar.phone.StatusBar;
Jason Monke59dc402018-08-16 12:05:01 -0400139import com.android.systemui.statusbar.policy.ConfigurationController;
140import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900141import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100142import com.android.systemui.statusbar.policy.ScrollAdapter;
Gus Prevas33619af2018-10-26 15:40:27 -0400143import com.android.systemui.tuner.TunerService;
Ned Burnsd4a69f72019-06-19 19:49:19 -0400144import com.android.systemui.util.Assert;
Selim Cinek67b22602014-03-10 15:40:16 +0100145
Selim Cinek707e2072017-06-30 18:32:40 +0200146import java.io.FileDescriptor;
147import java.io.PrintWriter;
Ned Burns61269442019-05-02 18:27:23 -0400148import java.lang.annotation.Retention;
Selim Cinek572bbd42014-04-25 16:43:27 +0200149import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800150import java.util.Collections;
151import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200152import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800153import java.util.List;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800154import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200155
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500156import javax.inject.Inject;
157import javax.inject.Named;
158
Selim Cinek67b22602014-03-10 15:40:16 +0100159/**
160 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
161 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400162public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter,
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700163 NotificationListContainer, ConfigurationListener, Dumpable,
164 DynamicPrivacyController.Listener {
Selim Cinek67b22602014-03-10 15:40:16 +0100165
Selim Cinekd35c2792016-01-21 13:20:57 -0800166 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800167 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100168 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200169 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
170 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200171 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100172 /**
173 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
174 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800175 private static final int INVALID_POINTER = -1;
Gus Prevase2d6f042018-10-17 15:25:30 -0400176 static final int NUM_SECTIONS = 2;
177 /**
178 * The distance in pixels between sections when the sections are directly adjacent (no visible
179 * gap is drawn between them). In this case we don't want to round their corners.
180 */
181 private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
Selim Cinekc3fec682019-06-06 18:11:07 -0700182 private final KeyguardBypassController mKeyguardBypassController;
Selim Cinek67b22602014-03-10 15:40:16 +0100183
Selim Cinek1408eb52014-06-02 14:45:38 +0200184 private ExpandHelper mExpandHelper;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400185 private final NotificationSwipeHelper mSwipeHelper;
Selim Cinek67b22602014-03-10 15:40:16 +0100186 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800187 private final Paint mBackgroundPaint = new Paint();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700188 private final boolean mShouldDrawNotificationBackground;
Selim Cinekda330ff2019-06-11 15:41:28 -0700189 private boolean mHighPriorityBeforeSpeedBump;
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500190 private final boolean mAllowLongPress;
Gus Prevasa18dc572019-01-14 16:11:22 -0500191 private boolean mDismissRtl;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100192
Selim Cinekbc243a92016-09-27 16:35:13 -0700193 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100194 private int mOwnScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -0500195 private View mScrollAnchorView;
196 private int mScrollAnchorViewY;
Selim Cinek67b22602014-03-10 15:40:16 +0100197 private int mMaxLayoutHeight;
198
199 private VelocityTracker mVelocityTracker;
200 private OverScroller mScroller;
Gus Prevascdc98342019-01-14 14:29:44 -0500201 /** Last Y position reported by {@link #mScroller}, used to calculate scroll delta. */
202 private int mLastScrollerY;
203 /**
204 * True if the max position was set to a known position on the last call to {@link #mScroller}.
205 */
206 private boolean mIsScrollerBoundSet;
Ricky Waicd35def2016-05-03 11:07:07 +0100207 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100208 private int mTouchSlop;
209 private int mMinimumVelocity;
210 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100211 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200212 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100213 private boolean mIsBeingDragged;
214 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200215 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700216 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100217 private boolean mTouchIsClick;
218 private float mInitialTouchX;
219 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100220
Selim Cinek67b22602014-03-10 15:40:16 +0100221 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100222 private int mContentHeight;
Lucas Dupin60661a62018-04-12 10:50:13 -0700223 private int mIntrinsicContentHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100224 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100225 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800226 private int mIncreasedPaddingBetweenElements;
shawnlin8e4e92c2018-04-12 18:47:24 +0800227 private int mMaxTopPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200228 private int mTopPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700229 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700230 private int mBottomInset = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +0800231 private float mQsExpansionFraction;
Selim Cinek67b22602014-03-10 15:40:16 +0100232
233 /**
234 * The algorithm which calculates the properties for our children
235 */
Muyuan Li87798022016-04-07 17:51:25 -0700236 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100237
Selim Cinek281c2022016-10-13 19:14:43 -0700238 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100239 private NotificationGroupManager mGroupManager;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500240 private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700241 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500242 private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
243 private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200244 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700245 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
246 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200247 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200248 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200249 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800250 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200251
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200252 /**
253 * The raw amount of the overScroll on the top, which is not rubber-banded.
254 */
255 private float mOverScrolledTopPixels;
256
257 /**
258 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
259 */
260 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900261 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200262 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200263 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100264 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200265 private boolean mNeedsAnimation;
266 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200267 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200268 private boolean mHideSensitiveNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200269 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200270 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200271 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200272 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200273 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700274 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200275 private boolean mExpandingNotification;
276 private boolean mExpandedInThisMotion;
shawnlin8e4e92c2018-04-12 18:47:24 +0800277 private boolean mShouldShowShelfOnly;
Muyuan Li84b45612016-06-01 11:05:08 -0700278 protected boolean mScrollingEnabled;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400279 protected FooterView mFooterView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700280 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400281 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700282 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200283
284 /**
285 * Was the scroller scrolled to the top when the down motion was observed?
286 */
287 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200288 /**
289 * The minimal amount of over scroll which is needed in order to switch to the quick settings
290 * when over scrolling on a expanded card.
291 */
292 private float mMinTopOverScrollToEscape;
293 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700294 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200295 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200296 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700297 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200298 private boolean mNeedViewResizeAnimation;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500299 private ExpandableView mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700300 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700301
Selim Cinek1408eb52014-06-02 14:45:38 +0200302 /**
303 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
304 * This is needed to avoid scrolling too far after the notification was collapsed in the same
305 * motion.
306 */
307 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000308 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700309 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200310
311 /**
312 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
313 * animating.
314 */
315 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700316 private boolean mDisallowDismissInThisMotion;
Selim Cineka59ecc32015-04-07 10:51:49 -0700317 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700318 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200319 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200320 = new ViewTreeObserver.OnPreDrawListener() {
321 @Override
322 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400323 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200324 updateChildren();
325 mChildrenUpdateRequested = false;
326 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200327 return true;
328 }
329 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500330 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100331 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100332 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700333 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek9dd0d042018-05-14 18:12:42 -0700334 private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700335 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
336 = new HashSet<>();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900337 private HeadsUpManagerPhone mHeadsUpManager;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800338 private final NotificationRoundnessManager mRoundnessManager;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700339 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700340 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200341 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700342 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700343 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800344 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700345 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800346 = new ViewTreeObserver.OnPreDrawListener() {
347 @Override
348 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700349 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800350 return true;
351 }
352 };
Gus Prevase2d6f042018-10-17 15:25:30 -0400353 private NotificationSection[] mSections = new NotificationSection[NUM_SECTIONS];
Selim Cinek614576e2016-01-20 10:54:09 -0800354 private boolean mAnimateNextBackgroundTop;
Gus Prevase2d6f042018-10-17 15:25:30 -0400355 private boolean mAnimateNextBackgroundBottom;
356 private boolean mAnimateNextSectionBoundsChange;
Selim Cinekd35c2792016-01-21 13:20:57 -0800357 private int mBgColor;
358 private float mDimAmount;
359 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800360 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400361 private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
Selim Cinekd35c2792016-01-21 13:20:57 -0800362 @Override
363 public void onAnimationEnd(Animator animation) {
364 mDimAnimator = null;
365 }
366 };
367 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
368 = new ValueAnimator.AnimatorUpdateListener() {
369
370 @Override
371 public void onAnimationUpdate(ValueAnimator animation) {
372 setDimAmount((Float) animation.getAnimatedValue());
373 }
374 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700375 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800376 private boolean mContinuousShadowUpdate;
Selim Cinekae55d832019-02-22 17:43:43 -0800377 private boolean mContinuousBackgroundUpdate;
Selim Cinek33223572016-02-19 19:32:22 -0800378 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
379 = new ViewTreeObserver.OnPreDrawListener() {
380
381 @Override
382 public boolean onPreDraw() {
383 updateViewShadows();
384 return true;
385 }
386 };
Selim Cinekae55d832019-02-22 17:43:43 -0800387 private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> {
388 updateBackground();
389 return true;
390 };
Selim Cinek33223572016-02-19 19:32:22 -0800391 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
392 @Override
393 public int compare(ExpandableView view, ExpandableView otherView) {
394 float endY = view.getTranslationY() + view.getActualHeight();
395 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
396 if (endY < otherEndY) {
397 return -1;
398 } else if (endY > otherEndY) {
399 return 1;
400 } else {
401 // The two notifications end at the same location
402 return 0;
403 }
404 }
405 };
Lucas Dupin64e2f572019-03-21 14:21:14 -0700406 private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
407 @Override
408 public void getOutline(View view, Outline outline) {
Selim Cinek195dfc52019-05-30 19:35:05 -0700409 if (mAmbientState.isHiddenAtAll()) {
410 float xProgress = mHideXInterpolator.getInterpolation(
411 (1 - mLinearHideAmount) * mBackgroundXFactor);
Selim Cinek9ad240c2019-04-08 19:07:15 -0700412 outline.setRoundRect(mBackgroundAnimationRect,
413 MathUtils.lerp(mCornerRadius / 2.0f, mCornerRadius,
414 xProgress));
Lucas Dupin64e2f572019-03-21 14:21:14 -0700415 } else {
416 ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
417 }
418 }
419 };
Selim Cinek25503252016-03-03 15:31:43 -0800420 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900421 private boolean mPulsing;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700422 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700423 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400424 private View mForcedScroll;
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200425
426 /**
Selim Cinek195dfc52019-05-30 19:35:05 -0700427 * @see #setHideAmount(float, float)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200428 */
Selim Cinek195dfc52019-05-30 19:35:05 -0700429 private float mInterpolatedHideAmount = 0f;
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200430
431 /**
Selim Cinek195dfc52019-05-30 19:35:05 -0700432 * @see #setHideAmount(float, float)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200433 */
Selim Cinek195dfc52019-05-30 19:35:05 -0700434 private float mLinearHideAmount = 0f;
Lucas Dupin439bd442018-06-12 15:05:28 -0700435
436 /**
437 * How fast the background scales in the X direction as a factor of the Y expansion.
438 */
439 private float mBackgroundXFactor = 1f;
Selim Cinek972123d2016-05-03 14:25:58 -0700440
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -0500441 private boolean mSwipingInProgress;
442
Lucas Dupine17ce522017-07-17 15:45:06 -0700443 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700444 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700445 private boolean mForwardScrollable;
446 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700447 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700448 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800449 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700450 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800451 private boolean mNoAmbient;
452 private final Rect mClipRect = new Rect();
453 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700454 private Rect mRequestedClipBounds;
455 private boolean mInHeadsUpPinnedMode;
456 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800457 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100458 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700459 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Gus Prevascdc98342019-01-14 14:29:44 -0500460 private Runnable mReflingAndAnimateScroll = () -> {
461 if (ANCHOR_SCROLLING) {
462 maybeReflingScroller();
463 }
464 animateScroll();
465 };
Selim Cinek0fe07392017-11-09 13:26:34 -0800466 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800467 private int mSidePaddings;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800468 private final Rect mBackgroundAnimationRect = new Rect();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800469 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
470 private int mHeadsUpInset;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700471 private HeadsUpAppearanceController mHeadsUpAppearanceController;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700472 private NotificationIconAreaController mIconAreaController;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400473 private final NotificationLockscreenUserManager mLockscreenUserManager =
474 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monke59dc402018-08-16 12:05:01 -0400475 private final Rect mTmpRect = new Rect();
476 private final NotificationEntryManager mEntryManager =
477 Dependency.get(NotificationEntryManager.class);
478 private final IStatusBarService mBarService = IStatusBarService.Stub.asInterface(
479 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Will Brockmane718d582019-01-17 16:38:38 -0500480 @VisibleForTesting
481 protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Jason Monke59dc402018-08-16 12:05:01 -0400482 private final NotificationRemoteInputManager mRemoteInputManager =
483 Dependency.get(NotificationRemoteInputManager.class);
484 private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
485
486 private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
487 private final LockscreenGestureLogger mLockscreenGestureLogger =
488 Dependency.get(LockscreenGestureLogger.class);
489 private final VisualStabilityManager mVisualStabilityManager =
490 Dependency.get(VisualStabilityManager.class);
491 protected boolean mClearAllEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +0100492
Selim Cinek195dfc52019-05-30 19:35:05 -0700493 private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
Jason Monke59dc402018-08-16 12:05:01 -0400494 private NotificationPanelView mNotificationPanel;
Jason Monk297c04e2018-08-23 17:16:59 -0400495 private final ShadeController mShadeController = Dependency.get(ShadeController.class);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400496
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400497 private final NotificationGutsManager
498 mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
Ned Burns9eb06332019-04-23 16:02:12 -0400499 private final NotificationSectionsManager mSectionsManager;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700500 private boolean mAnimateBottomOnLayout;
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700501 private float mLastSentAppear;
502 private float mLastSentExpandedHeight;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400503
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500504 @Inject
505 public NotificationStackScrollLayout(
506 @Named(VIEW_CONTEXT) Context context,
507 AttributeSet attrs,
Lucas Dupin00be88f2019-01-03 17:50:52 -0800508 @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
Selim Cinek3d6ae232019-01-04 14:14:33 -0800509 NotificationRoundnessManager notificationRoundnessManager,
Ned Burns9eb06332019-04-23 16:02:12 -0400510 DynamicPrivacyController dynamicPrivacyController,
Ned Burns2c74c2a2019-06-13 19:06:47 -0400511 ConfigurationController configurationController,
Ned Burns7eeccdd2019-05-15 14:50:11 -0400512 ActivityStarter activityStarter,
Selim Cinekc3fec682019-06-06 18:11:07 -0700513 StatusBarStateController statusBarStateController,
514 HeadsUpManagerPhone headsUpManager,
515 KeyguardBypassController keyguardBypassController) {
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500516 super(context, attrs, 0, 0);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800517 Resources res = getResources();
518
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500519 mAllowLongPress = allowLongPress;
520
Gus Prevase2d6f042018-10-17 15:25:30 -0400521 for (int i = 0; i < NUM_SECTIONS; i++) {
522 mSections[i] = new NotificationSection(this);
523 }
Selim Cinekc3fec682019-06-06 18:11:07 -0700524 mRoundnessManager = notificationRoundnessManager;
Gus Prevase2d6f042018-10-17 15:25:30 -0400525
Selim Cinekc3fec682019-06-06 18:11:07 -0700526 mHeadsUpManager = headsUpManager;
527 mHeadsUpManager.addListener(mRoundnessManager);
528 mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
529 mKeyguardBypassController = keyguardBypassController;
Ned Burns61269442019-05-02 18:27:23 -0400530
Ned Burns9eb06332019-04-23 16:02:12 -0400531 mSectionsManager =
532 new NotificationSectionsManager(
533 this,
534 activityStarter,
Ned Burns7eeccdd2019-05-15 14:50:11 -0400535 statusBarStateController,
Ned Burns2c74c2a2019-06-13 19:06:47 -0400536 configurationController,
Ned Burns9eb06332019-04-23 16:02:12 -0400537 NotificationUtils.useNewInterruptionModel(context));
Ned Burns2c74c2a2019-06-13 19:06:47 -0400538 mSectionsManager.initialize(LayoutInflater.from(context));
Ned Burns61269442019-05-02 18:27:23 -0400539 mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
540 // Leave the shade open if there will be other notifs left over to clear
541 final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
542 clearNotifications(ROWS_GENTLE, closeShade);
543 });
544
Selim Cinekc3fec682019-06-06 18:11:07 -0700545 mAmbientState = new AmbientState(context, mSectionsManager, mHeadsUpManager);
Selim Cinekd35c2792016-01-21 13:20:57 -0800546 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800547 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
548 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400549 mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
Selim Cinek1cf41c12014-08-12 20:06:19 +0200550 minHeight, maxHeight);
551 mExpandHelper.setEventSource(this);
552 mExpandHelper.setScrollAdapter(this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400553 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
554 getContext(), mMenuEventListener);
Muyuan Li333a4fc2016-04-16 17:13:46 -0700555 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100556 initView(context);
Dave Mankoffdde5ee62019-05-02 17:36:11 -0400557 mFalsingManager = FalsingManagerFactory.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800558 mShouldDrawNotificationBackground =
559 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700560 mFadeNotificationsOnDismiss =
561 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Selim Cinek29aab962018-02-27 17:05:45 -0800562 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
563 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700564 addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded);
Lucas Dupin64e2f572019-03-21 14:21:14 -0700565 setOutlineProvider(mOutlineProvider);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800566
Rohan Shah524cf7b2018-03-15 14:40:02 -0700567 // Blocking helper manager wants to know the expanded state, update as well.
568 NotificationBlockingHelperManager blockingHelperManager =
569 Dependency.get(NotificationBlockingHelperManager.class);
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700570 addOnExpandedHeightChangedListener((height, unused) -> {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700571 blockingHelperManager.setNotificationShadeExpanded(height);
572 });
573
Selim Cinek195dfc52019-05-30 19:35:05 -0700574 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
575 setWillNotDraw(!willDraw);
Lucas Dupind285cf02018-01-18 09:18:23 -0800576 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100577 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100578 mDebugPaint = new Paint();
579 mDebugPaint.setColor(0xffff0000);
580 mDebugPaint.setStrokeWidth(2);
581 mDebugPaint.setStyle(Paint.Style.STROKE);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500582 mDebugPaint.setTextSize(25f);
Selim Cinek67b22602014-03-10 15:40:16 +0100583 }
Jason Monke59dc402018-08-16 12:05:01 -0400584 mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
Gus Prevas33619af2018-10-26 15:40:27 -0400585
586 TunerService tunerService = Dependency.get(TunerService.class);
587 tunerService.addTunable((key, newValue) -> {
Selim Cinekda330ff2019-06-11 15:41:28 -0700588 if (key.equals(HIGH_PRIORITY)) {
589 mHighPriorityBeforeSpeedBump = "1".equals(newValue);
Gus Prevasa18dc572019-01-14 16:11:22 -0500590 } else if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) {
591 updateDismissRtlSetting("1".equals(newValue));
Gus Prevas33619af2018-10-26 15:40:27 -0400592 }
Selim Cinekda330ff2019-06-11 15:41:28 -0700593 }, HIGH_PRIORITY, Settings.Secure.NOTIFICATION_DISMISS_RTL);
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500594
595 mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
596 @Override
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800597 public void onPostEntryUpdated(NotificationEntry entry) {
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500598 if (!entry.notification.isClearable()) {
599 // The user may have performed a dismiss action on the notification, since it's
600 // not clearable we should snap it back.
601 snapViewIfNeeded(entry);
602 }
603 }
604 });
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700605 dynamicPrivacyController.addListener(this);
Jason Monke59dc402018-08-16 12:05:01 -0400606 }
607
Gus Prevasa18dc572019-01-14 16:11:22 -0500608 private void updateDismissRtlSetting(boolean dismissRtl) {
609 mDismissRtl = dismissRtl;
610 for (int i = 0; i < getChildCount(); i++) {
611 View child = getChildAt(i);
612 if (child instanceof ExpandableNotificationRow) {
613 ((ExpandableNotificationRow) child).setDismissRtl(dismissRtl);
614 }
615 }
616 }
617
Jason Monke59dc402018-08-16 12:05:01 -0400618 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400619 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400620 protected void onFinishInflate() {
621 super.onFinishInflate();
622
623 inflateEmptyShadeView();
624 inflateFooterView();
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400625 mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500626 if (mAllowLongPress) {
Steve Elliottff6c25e2019-05-30 17:38:52 -0400627 setLongPressListener(mNotificationGutsManager::openGuts);
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500628 }
Jason Monke59dc402018-08-16 12:05:01 -0400629 }
630
Selim Cinek34518f62019-02-28 19:41:18 -0800631 /**
632 * @return the height at which we will wake up when pulsing
633 */
Selim Cinekb0fada62019-06-17 19:03:59 -0700634 public float getWakeUpHeight() {
Selim Cinek3d6ae232019-01-04 14:14:33 -0800635 ActivatableNotificationView firstChild = getFirstChildWithBackground();
636 if (firstChild != null) {
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700637 if (mKeyguardBypassController.getBypassEnabled()) {
638 return firstChild.getHeadsUpHeightWithoutHeader();
639 } else {
640 return firstChild.getCollapsedHeight();
641 }
Selim Cinek3d6ae232019-01-04 14:14:33 -0800642 }
Selim Cinek34518f62019-02-28 19:41:18 -0800643 return 0f;
Selim Cinek3d6ae232019-01-04 14:14:33 -0800644 }
645
Jason Monke59dc402018-08-16 12:05:01 -0400646 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400647 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400648 public void onDensityOrFontScaleChanged() {
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800649 reinflateViews();
650 }
651
652 private void reinflateViews() {
Jason Monke59dc402018-08-16 12:05:01 -0400653 inflateFooterView();
654 inflateEmptyShadeView();
655 updateFooter();
Ned Burns2c74c2a2019-06-13 19:06:47 -0400656 mSectionsManager.reinflateViews(LayoutInflater.from(mContext));
Jason Monke59dc402018-08-16 12:05:01 -0400657 }
658
659 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400660 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400661 public void onThemeChanged() {
Lucas Dupin89233c42019-06-10 18:00:22 -0700662 final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
Jason Monke59dc402018-08-16 12:05:01 -0400663 updateDecorViews(useDarkText);
664
665 updateFooter();
666 }
667
Fabian Kozynskid254b192019-02-05 13:42:58 -0500668 @Override
669 public void onOverlayChanged() {
670 int newRadius = mContext.getResources().getDimensionPixelSize(
671 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
672 if (mCornerRadius != newRadius) {
673 mCornerRadius = newRadius;
674 invalidate();
675 }
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800676 reinflateViews();
Fabian Kozynskid254b192019-02-05 13:42:58 -0500677 }
678
Jason Monke59dc402018-08-16 12:05:01 -0400679 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400680 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400681 public void updateFooter() {
Ned Burns61269442019-05-02 18:27:23 -0400682 boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL);
Jason Monke59dc402018-08-16 12:05:01 -0400683 boolean showFooterView = (showDismissView ||
684 mEntryManager.getNotificationData().getActiveNotifications().size() != 0)
685 && mStatusBarState != StatusBarState.KEYGUARD
686 && !mRemoteInputManager.getController().isRemoteInputActive();
687
688 updateFooterView(showFooterView, showDismissView);
689 }
690
691 /**
692 * Return whether there are any clearable notifications
693 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400694 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -0400695 public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
Jason Monke59dc402018-08-16 12:05:01 -0400696 int childCount = getChildCount();
697 for (int i = 0; i < childCount; i++) {
698 View child = getChildAt(i);
699 if (!(child instanceof ExpandableNotificationRow)) {
700 continue;
701 }
Ned Burns61269442019-05-02 18:27:23 -0400702 final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
703 if (row.canViewBeDismissed() && matchesSelection(row, selection)) {
Jason Monke59dc402018-08-16 12:05:01 -0400704 return true;
705 }
706 }
707 return false;
708 }
709
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400710 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400711 public RemoteInputController.Delegate createDelegate() {
Jason Monke59dc402018-08-16 12:05:01 -0400712 return new RemoteInputController.Delegate() {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500713 public void setRemoteInputActive(NotificationEntry entry,
Jason Monke59dc402018-08-16 12:05:01 -0400714 boolean remoteInputActive) {
715 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
Evan Laird94492852018-10-25 13:43:01 -0400716 entry.notifyHeightChanged(true /* needsAnimation */);
Jason Monke59dc402018-08-16 12:05:01 -0400717 updateFooter();
718 }
719
Ned Burnsf81c4c42019-01-07 14:10:43 -0500720 public void lockScrollTo(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400721 NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
Jason Monke59dc402018-08-16 12:05:01 -0400722 }
723
724 public void requestDisallowLongPressAndDismiss() {
725 requestDisallowLongPress();
726 requestDisallowDismiss();
727 }
728 };
Selim Cinek67b22602014-03-10 15:40:16 +0100729 }
730
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900731 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400732 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400733 protected void onAttachedToWindow() {
734 super.onAttachedToWindow();
Beverly8fdb5332019-02-04 14:29:49 -0500735 ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class))
736 .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
Jason Monke59dc402018-08-16 12:05:01 -0400737 Dependency.get(ConfigurationController.class).addCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400738 }
739
740 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400741 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400742 protected void onDetachedFromWindow() {
743 super.onDetachedFromWindow();
Jason Monkaf08c152018-12-04 11:12:39 -0500744 Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Jason Monke59dc402018-08-16 12:05:01 -0400745 Dependency.get(ConfigurationController.class).removeCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400746 }
747
748 @Override
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400749 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -0800750 public NotificationSwipeActionHelper getSwipeActionHelper() {
751 return mSwipeHelper;
752 }
753
Selim Cinek67b22602014-03-10 15:40:16 +0100754 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400755 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupinf03e7522018-06-25 16:21:13 -0700756 public void onUiModeChanged() {
757 mBgColor = mContext.getColor(R.color.notification_shade_background_color);
758 updateBackgroundDimming();
Selim Cinekab9c7b22018-12-11 18:15:47 -0800759 mShelf.onUiModeChanged();
Ned Burns9eb06332019-04-23 16:02:12 -0400760 mSectionsManager.onUiModeChanged();
Lucas Dupinf03e7522018-06-25 16:21:13 -0700761 }
762
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400763 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Selim Cinek67b22602014-03-10 15:40:16 +0100764 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800765 if (mShouldDrawNotificationBackground
Gus Prevase2d6f042018-10-17 15:25:30 -0400766 && (mSections[0].getCurrentBounds().top
767 < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
Selim Cinekc1d9ab22019-05-21 18:08:30 -0700768 || mAmbientState.isDozing())) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800769 drawBackground(canvas);
Gus Prevas211181532018-12-13 14:49:33 -0500770 } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
771 drawHeadsUpBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700772 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800773
Selim Cinek67b22602014-03-10 15:40:16 +0100774 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800775 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100776 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800777 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100778 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200779 y = getHeight() - getEmptyBottomMargin();
780 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100781 }
782 }
783
Gus Prevas0fa58d62019-01-11 13:58:40 -0500784 @Override
785 public void draw(Canvas canvas) {
786 super.draw(canvas);
787
788 if (DEBUG && ANCHOR_SCROLLING) {
789 if (mScrollAnchorView instanceof ExpandableNotificationRow) {
790 canvas.drawRect(0,
791 mScrollAnchorView.getTranslationY(),
792 getWidth(),
793 mScrollAnchorView.getTranslationY()
794 + ((ExpandableNotificationRow) mScrollAnchorView).getActualHeight(),
795 mDebugPaint);
796 canvas.drawText(Integer.toString(mScrollAnchorViewY), getWidth() - 200,
797 mScrollAnchorView.getTranslationY() + 30, mDebugPaint);
798 int y = (int) mShelf.getTranslationY();
799 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
800 }
Gus Prevascdc98342019-01-14 14:29:44 -0500801 canvas.drawText(Integer.toString(getMaxNegativeScrollAmount()), getWidth() - 100,
Selim Cinekdb6d6df2019-05-22 20:39:59 -0700802 getTopPadding() + 30, mDebugPaint);
Gus Prevascdc98342019-01-14 14:29:44 -0500803 canvas.drawText(Integer.toString(getMaxPositiveScrollAmount()), getWidth() - 100,
804 getHeight() - 30, mDebugPaint);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500805 }
806 }
807
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400808 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupind285cf02018-01-18 09:18:23 -0800809 private void drawBackground(Canvas canvas) {
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700810 int lockScreenLeft = mSidePaddings;
811 int lockScreenRight = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -0400812 int lockScreenTop = mSections[0].getCurrentBounds().top;
813 int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom;
Selim Cinek195dfc52019-05-30 19:35:05 -0700814 int hiddenLeft = getWidth() / 2;
815 int hiddenTop = mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800816
Selim Cinek195dfc52019-05-30 19:35:05 -0700817 float yProgress = 1 - mInterpolatedHideAmount;
818 float xProgress = mHideXInterpolator.getInterpolation(
819 (1 - mLinearHideAmount) * mBackgroundXFactor);
Lucas Dupin60661a62018-04-12 10:50:13 -0700820
Selim Cinek195dfc52019-05-30 19:35:05 -0700821 int left = (int) MathUtils.lerp(hiddenLeft, lockScreenLeft, xProgress);
822 int right = (int) MathUtils.lerp(hiddenLeft, lockScreenRight, xProgress);
823 int top = (int) MathUtils.lerp(hiddenTop, lockScreenTop, yProgress);
824 int bottom = (int) MathUtils.lerp(hiddenTop, lockScreenBottom, yProgress);
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700825 mBackgroundAnimationRect.set(
Gus Prevase2d6f042018-10-17 15:25:30 -0400826 left,
827 top,
828 right,
829 bottom);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200830
Gus Prevase2d6f042018-10-17 15:25:30 -0400831 int backgroundTopAnimationOffset = top - lockScreenTop;
832 // TODO(kprevas): this may not be necessary any more since we don't display the shelf in AOD
833 boolean anySectionHasVisibleChild = false;
834 for (NotificationSection section : mSections) {
835 if (section.getFirstVisibleChild() != null) {
836 anySectionHasVisibleChild = true;
837 break;
838 }
839 }
Selim Cinekc1d9ab22019-05-21 18:08:30 -0700840 if (!mAmbientState.isDozing() || anySectionHasVisibleChild) {
Gus Prevase2d6f042018-10-17 15:25:30 -0400841 drawBackgroundRects(canvas, left, right, top, backgroundTopAnimationOffset);
Lucas Dupind285cf02018-01-18 09:18:23 -0800842 }
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700843
Lucas Dupin16cfe452018-02-08 13:14:50 -0800844 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800845 }
846
Gus Prevase2d6f042018-10-17 15:25:30 -0400847 /**
848 * Draws round rects for each background section.
849 *
850 * We want to draw a round rect for each background section as defined by {@link #mSections}.
851 * However, if two sections are directly adjacent with no gap between them (e.g. on the
852 * lockscreen where the shelf can appear directly below the high priority section, or while
853 * scrolling the shade so that the top of the shelf is right at the bottom of the high priority
854 * section), we don't want to round the adjacent corners.
855 *
856 * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we
857 * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect.
858 * This method tracks the top of each rect we need to draw, then iterates through the visible
859 * sections. If a section is not adjacent to the previous section, we draw the previous rect
860 * behind the sections we've accumulated up to that point, then start a new rect at the top of
861 * the current section. When we're done iterating we will always have one rect left to draw.
862 */
863 private void drawBackgroundRects(Canvas canvas, int left, int right, int top,
864 int animationYOffset) {
865 int backgroundRectTop = top;
866 int lastSectionBottom =
867 mSections[0].getCurrentBounds().bottom + animationYOffset;
Selim Cinek0acf4302019-06-11 12:43:03 -0700868 int currentLeft = left;
869 int currentRight = right;
Selim Cinekae55d832019-02-22 17:43:43 -0800870 boolean first = true;
Gus Prevase2d6f042018-10-17 15:25:30 -0400871 for (NotificationSection section : mSections) {
872 if (section.getFirstVisibleChild() == null) {
873 continue;
874 }
875 int sectionTop = section.getCurrentBounds().top + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800876 int ownLeft = Math.min(Math.max(left, section.getCurrentBounds().left), right);
Selim Cinekd0b48e32019-05-24 20:49:23 -0700877 int ownRight = Math.max(Math.min(right, section.getCurrentBounds().right), ownLeft);
Gus Prevase2d6f042018-10-17 15:25:30 -0400878 // If sections are directly adjacent to each other, we don't want to draw them
879 // as separate roundrects, as the rounded corners right next to each other look
880 // bad.
Selim Cinekae55d832019-02-22 17:43:43 -0800881 if (sectionTop - lastSectionBottom > DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX
Selim Cinek0acf4302019-06-11 12:43:03 -0700882 || ((currentLeft != ownLeft || currentRight != ownRight) && !first)) {
883 canvas.drawRoundRect(currentLeft,
Gus Prevase2d6f042018-10-17 15:25:30 -0400884 backgroundRectTop,
Selim Cinek0acf4302019-06-11 12:43:03 -0700885 currentRight,
Gus Prevase2d6f042018-10-17 15:25:30 -0400886 lastSectionBottom,
887 mCornerRadius, mCornerRadius, mBackgroundPaint);
888 backgroundRectTop = sectionTop;
889 }
Selim Cinek0acf4302019-06-11 12:43:03 -0700890 currentLeft = ownLeft;
891 currentRight = ownRight;
Gus Prevase2d6f042018-10-17 15:25:30 -0400892 lastSectionBottom =
893 section.getCurrentBounds().bottom + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800894 first = false;
Gus Prevase2d6f042018-10-17 15:25:30 -0400895 }
Selim Cinek0acf4302019-06-11 12:43:03 -0700896 canvas.drawRoundRect(currentLeft,
Gus Prevase2d6f042018-10-17 15:25:30 -0400897 backgroundRectTop,
Selim Cinek0acf4302019-06-11 12:43:03 -0700898 currentRight,
Gus Prevase2d6f042018-10-17 15:25:30 -0400899 lastSectionBottom,
900 mCornerRadius, mCornerRadius, mBackgroundPaint);
901 }
902
Gus Prevas211181532018-12-13 14:49:33 -0500903 private void drawHeadsUpBackground(Canvas canvas) {
904 int left = mSidePaddings;
905 int right = getWidth() - mSidePaddings;
906
907 float top = getHeight();
908 float bottom = 0;
909 int childCount = getChildCount();
910 for (int i = 0; i < childCount; i++) {
911 View child = getChildAt(i);
912 if (child.getVisibility() != View.GONE
913 && child instanceof ExpandableNotificationRow) {
914 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
shawnlin3a950c32019-05-15 20:06:10 +0800915 if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0
916 && row.getProvider().shouldShowGutsOnSnapOpen()) {
Gus Prevas211181532018-12-13 14:49:33 -0500917 top = Math.min(top, row.getTranslationY());
918 bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
919 }
920 }
921 }
922
923 if (top < bottom) {
924 canvas.drawRoundRect(
925 left, top, right, bottom,
926 mCornerRadius, mCornerRadius, mBackgroundPaint);
927 }
928 }
929
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400930 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd35c2792016-01-21 13:20:57 -0800931 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800932 // No need to update the background color if it's not being drawn.
933 if (!mShouldDrawNotificationBackground) {
934 return;
935 }
936
Lucas Dupinb561eda2018-04-09 17:25:04 -0700937 // Interpolate between semi-transparent notification panel background color
938 // and white AOD separator.
Lucas Dupinf03e7522018-06-25 16:21:13 -0700939 float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */,
Selim Cinek195dfc52019-05-30 19:35:05 -0700940 mLinearHideAmount);
Felka Chang97bc8d52019-05-29 02:51:15 +0800941 int color = ColorUtils.blendARGB(mBgColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800942
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100943 if (mCachedBackgroundColor != color) {
944 mCachedBackgroundColor = color;
945 mBackgroundPaint.setColor(color);
946 invalidate();
947 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800948 }
949
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400950 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +0100951 private void initView(Context context) {
952 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100953 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200954 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100955 final ViewConfiguration configuration = ViewConfiguration.get(context);
956 mTouchSlop = configuration.getScaledTouchSlop();
957 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
958 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100959 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700960
961 Resources res = context.getResources();
962 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800963 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700964 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700965 mPaddingBetweenElements = Math.max(1,
966 res.getDimensionPixelSize(R.dimen.notification_divider_height));
967 mIncreasedPaddingBetweenElements =
968 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
969 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200970 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800971 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700972 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800973 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700974 mMinInteractionHeight = res.getDimensionPixelSize(
975 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800976 mCornerRadius = res.getDimensionPixelSize(
977 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800978 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
979 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200980 }
981
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400982 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekaef92ef2014-06-06 18:06:04 +0200983 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700984 notifyHeightChangeListener(view, false /* needsAnimation */);
985 }
986
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400987 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -0700988 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +0200989 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700990 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200991 }
Selim Cinek67b22602014-03-10 15:40:16 +0100992 }
993
Selim Cinekb0fada62019-06-17 19:03:59 -0700994 public boolean isPulseExpanding() {
995 return mAmbientState.isPulseExpanding();
996 }
997
Selim Cinek67b22602014-03-10 15:40:16 +0100998 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400999 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001000 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1001 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -08001002
1003 int width = MeasureSpec.getSize(widthMeasureSpec);
1004 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
1005 MeasureSpec.getMode(widthMeasureSpec));
Evan Lairdeb7dbd52018-06-28 13:17:25 -04001006 // Don't constrain the height of the children so we know how big they'd like to be
1007 int childHeightSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
1008 MeasureSpec.UNSPECIFIED);
1009
Selim Cinekfa760d42016-05-10 15:50:53 -04001010 // We need to measure all children even the GONE ones, such that the heights are calculated
1011 // correctly as they are used to calculate how many we can fit on the screen.
1012 final int size = getChildCount();
1013 for (int i = 0; i < size; i++) {
Evan Lairdeb7dbd52018-06-28 13:17:25 -04001014 measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -04001015 }
Selim Cinek67b22602014-03-10 15:40:16 +01001016 }
1017
1018 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001019 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001020 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +01001021 // we layout all our children centered on the top
1022 float centerX = getWidth() / 2.0f;
1023 for (int i = 0; i < getChildCount(); i++) {
1024 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -04001025 // We need to layout all children even the GONE ones, such that the heights are
1026 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +01001027 float width = child.getMeasuredWidth();
1028 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +01001029 child.layout((int) (centerX - width / 2.0f),
1030 0,
1031 (int) (centerX + width / 2.0f),
1032 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +01001033 }
Jorim Jaggi1d480692014-05-20 19:41:58 +02001034 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +01001035 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02001036 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +02001037 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -08001038 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -07001039 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +01001040 }
1041
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001042 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5bc852a2015-12-21 12:19:09 -08001043 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
1044 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +02001045 mNeedViewResizeAnimation = true;
1046 mNeedsAnimation = true;
1047 }
Selim Cineka5e211b2014-08-11 17:35:48 +02001048 }
1049
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001050 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekdb167372016-11-17 15:41:17 -08001051 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
1052 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001053 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +02001054 }
1055
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001056 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001057 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001058 public void setChildLocationsChangedListener(
1059 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001060 mListener = listener;
1061 }
1062
Selim Cineka7d4f822016-12-06 14:34:47 -08001063 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001064 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001065 public boolean isInVisibleLocation(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001066 ExpandableNotificationRow row = entry.getRow();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001067 ExpandableViewState childViewState = row.getViewState();
1068
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001069 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001070 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001071 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +01001072 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001073 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +01001074 }
Selim Cineka7d4f822016-12-06 14:34:47 -08001075 if (row.getVisibility() != View.VISIBLE) {
1076 return false;
1077 }
1078 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001079 }
1080
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001081 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinek67b22602014-03-10 15:40:16 +01001082 private void setMaxLayoutHeight(int maxLayoutHeight) {
1083 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -07001084 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001085 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +01001086 }
1087
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001088 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001089 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001090 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -07001091 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001092 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +01001093 }
1094
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001095 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinekbc243a92016-09-27 16:35:13 -07001096 private void updateAlgorithmLayoutMinHeight() {
shawnlinc3457912018-05-15 16:39:56 +08001097 mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001098 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -07001099 }
1100
Selim Cinek67b22602014-03-10 15:40:16 +01001101 /**
1102 * Updates the children views according to the stack scroll algorithm. Call this whenever
1103 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
1104 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001105 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001106 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -08001107 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -08001108 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
1109 ? 0
1110 : mScroller.getCurrVelocity());
Gus Prevas0fa58d62019-01-11 13:58:40 -05001111 if (ANCHOR_SCROLLING) {
1112 mAmbientState.setAnchorViewIndex(indexOfChild(mScrollAnchorView));
1113 mAmbientState.setAnchorViewY(mScrollAnchorViewY);
1114 } else {
1115 mAmbientState.setScrollY(mOwnScrollY);
1116 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001117 mStackScrollAlgorithm.resetViewStates(mAmbientState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001118 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02001119 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +01001120 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +02001121 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +01001122 }
1123 }
1124
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001125 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc383fd02016-10-21 15:31:26 -07001126 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -07001127 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001128 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -07001129 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
1130 updateBackground();
1131 }
Selim Cinekc383fd02016-10-21 15:31:26 -07001132 }
1133
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001134 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001135 private void updateClippingToTopRoundedCorner() {
Arthur Hungc0ef5652018-05-22 14:00:42 +08001136 Float clipStart = (float) mTopPadding
Jason Monke59dc402018-08-16 12:05:01 -04001137 + mStackTranslation
1138 + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001139 Float clipEnd = clipStart + mCornerRadius;
1140 boolean first = true;
1141 for (int i = 0; i < getChildCount(); i++) {
1142 ExpandableView child = (ExpandableView) getChildAt(i);
1143 if (child.getVisibility() == GONE) {
1144 continue;
1145 }
1146 float start = child.getTranslationY();
Arthur Hungc0ef5652018-05-22 14:00:42 +08001147 float end = start + child.getActualHeight();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001148 boolean clip = clipStart > start && clipStart < end
1149 || clipEnd >= start && clipEnd <= end;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001150 clip &= !(first && isScrolledToTop());
Selim Cinekeccf4942018-05-30 09:55:36 -07001151 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
1152 : ExpandableView.NO_ROUNDNESS);
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001153 first = false;
1154 }
1155 }
1156
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001157 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek3776fe02016-02-04 13:32:43 -08001158 private void updateScrollStateForAddedChildren() {
1159 if (mChildrenToAddAnimated.isEmpty()) {
1160 return;
1161 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001162 if (!ANCHOR_SCROLLING) {
1163 for (int i = 0; i < getChildCount(); i++) {
1164 ExpandableView child = (ExpandableView) getChildAt(i);
1165 if (mChildrenToAddAnimated.contains(child)) {
1166 int startingPosition = getPositionInLinearLayout(child);
1167 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
1168 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
1169 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
1170 int childHeight = getIntrinsicHeight(child) + padding;
1171 if (startingPosition < mOwnScrollY) {
1172 // This child starts off screen, so let's keep it offscreen to keep the
1173 // others visible
Selim Cinek3776fe02016-02-04 13:32:43 -08001174
Gus Prevas0fa58d62019-01-11 13:58:40 -05001175 setOwnScrollY(mOwnScrollY + childHeight);
1176 }
Selim Cinek3776fe02016-02-04 13:32:43 -08001177 }
1178 }
1179 }
1180 clampScrollPosition();
1181 }
1182
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001183 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001184 private void updateForcedScroll() {
1185 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
1186 || !mForcedScroll.isAttachedToWindow())) {
1187 mForcedScroll = null;
1188 }
1189 if (mForcedScroll != null) {
1190 ExpandableView expandableView = (ExpandableView) mForcedScroll;
1191 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
1192 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -07001193 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -04001194
Gus Prevas0fa58d62019-01-11 13:58:40 -05001195 if (ANCHOR_SCROLLING) {
1196 // TODO
1197 } else {
1198 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -07001199
Gus Prevas0fa58d62019-01-11 13:58:40 -05001200 // Only apply the scroll if we're scrolling the view upwards, or the view is so
1201 // far up that it is not visible anymore.
1202 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1203 setOwnScrollY(targetScroll);
1204 }
Adrian Roos181385c2016-05-05 17:45:44 -04001205 }
1206 }
1207 }
1208
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001209 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek319bdc42014-05-01 23:01:58 +02001210 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +02001211 if (!mChildrenUpdateRequested) {
1212 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
1213 mChildrenUpdateRequested = true;
1214 invalidate();
1215 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001216 }
1217
Robert Snoeberger168949a2019-04-18 09:39:42 -04001218 /**
1219 * Returns best effort count of visible notifications.
1220 */
1221 public int getVisibleNotificationCount() {
1222 int count = 0;
1223 for (int i = 0; i < getChildCount(); i++) {
1224 final View child = getChildAt(i);
1225 if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
1226 count++;
1227 }
1228 }
1229 return count;
1230 }
1231
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001232 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001233 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +02001234 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +01001235 }
1236
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001237 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekf7a14c02014-07-07 14:01:46 +02001238 private void clampScrollPosition() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001239 if (ANCHOR_SCROLLING) {
1240 // TODO
1241 } else {
1242 int scrollRange = getScrollRange();
1243 if (scrollRange < mOwnScrollY) {
1244 setOwnScrollY(scrollRange);
1245 }
Selim Cinek67b22602014-03-10 15:40:16 +01001246 }
1247 }
1248
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001249 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001250 public int getTopPadding() {
1251 return mTopPadding;
1252 }
1253
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001254 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001255 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupin00be88f2019-01-03 17:50:52 -08001256 if (mTopPadding != topPadding) {
1257 mTopPadding = topPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001258 updateAlgorithmHeightAndPadding();
1259 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +02001260 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001261 mTopPaddingNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04001262 mNeedsAnimation = true;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001263 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001264 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -07001265 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001266 }
1267 }
1268
1269 /**
Selim Cinekbc243a92016-09-27 16:35:13 -07001270 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001271 *
Selim Cinekbc243a92016-09-27 16:35:13 -07001272 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001273 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001274 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbc243a92016-09-27 16:35:13 -07001275 public void setExpandedHeight(float height) {
1276 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001277 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001278 int minExpansionHeight = getMinExpansionHeight();
1279 if (height < minExpansionHeight) {
1280 mClipRect.left = 0;
1281 mClipRect.right = getWidth();
1282 mClipRect.top = 0;
1283 mClipRect.bottom = (int) height;
1284 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001285 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001286 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001287 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001288 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001289 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -07001290 float translationY;
1291 float appearEndPosition = getAppearEndPosition();
1292 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001293 float appearFraction = 1.0f;
shawnlin5be1f7c2018-05-21 20:50:54 +08001294 boolean appearing = height < appearEndPosition;
1295 mAmbientState.setAppearing(appearing);
1296 if (!appearing) {
Selim Cinekbc243a92016-09-27 16:35:13 -07001297 translationY = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +08001298 if (mShouldShowShelfOnly) {
1299 stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
1300 } else if (mQsExpanded) {
1301 int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
1302 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
1303 if (stackStartPosition <= stackEndPosition) {
1304 stackHeight = stackEndPosition;
1305 } else {
1306 stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
1307 stackEndPosition, mQsExpansionFraction);
1308 }
1309 } else {
1310 stackHeight = (int) height;
1311 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001312 } else {
Selim Cinekc7e4cb52019-06-20 15:41:45 -07001313 appearFraction = calculateAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -07001314 if (appearFraction >= 0) {
1315 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
1316 appearFraction);
1317 } else {
1318 // This may happen when pushing up a heads up. We linearly push it up from the
1319 // start
1320 translationY = height - appearStartPosition + getExpandTranslationStart();
1321 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001322 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001323 stackHeight =
1324 getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001325 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
1326 } else {
1327 stackHeight = (int) (height - translationY);
1328 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001329 }
1330 if (stackHeight != mCurrentStackHeight) {
1331 mCurrentStackHeight = stackHeight;
1332 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +02001333 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001334 }
Selim Cinek94c2d822016-07-13 18:50:04 -07001335 setStackTranslation(translationY);
Selim Cinekc7e4cb52019-06-20 15:41:45 -07001336 notifyAppearChangedListeners();
1337 }
1338
1339 private void notifyAppearChangedListeners() {
1340 float appear;
1341 float expandAmount;
1342 if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) {
1343 appear = calculateAppearFractionBypass();
1344 expandAmount = getPulseHeight();
1345 } else {
1346 appear = MathUtils.saturate(calculateAppearFraction(mExpandedHeight));
1347 expandAmount = mExpandedHeight;
1348 }
1349 if (appear != mLastSentAppear || expandAmount != mLastSentExpandedHeight) {
1350 mLastSentAppear = appear;
1351 mLastSentExpandedHeight = expandAmount;
1352 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
1353 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
1354 listener.accept(expandAmount, appear);
1355 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001356 }
Selim Cinekcafa87f2016-10-26 17:00:17 -07001357 }
1358
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001359 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001360 private void setRequestedClipBounds(Rect clipRect) {
1361 mRequestedClipBounds = clipRect;
1362 updateClipping();
1363 }
1364
Lucas Dupin60661a62018-04-12 10:50:13 -07001365 /**
1366 * Return the height of the content ignoring the footer.
1367 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001368 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -07001369 public int getIntrinsicContentHeight() {
1370 return mIntrinsicContentHeight;
1371 }
1372
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001373 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001374 public void updateClipping() {
1375 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
1376 && !mHeadsUpAnimatingAway;
Lucas Dupin64e2f572019-03-21 14:21:14 -07001377 boolean clipToOutline = false;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001378 if (mIsClipped != clipped) {
1379 mIsClipped = clipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001380 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08001381
Selim Cinek195dfc52019-05-30 19:35:05 -07001382 if (mAmbientState.isHiddenAtAll()) {
Lucas Dupin64e2f572019-03-21 14:21:14 -07001383 clipToOutline = true;
1384 invalidateOutline();
Selim Cinek195dfc52019-05-30 19:35:05 -07001385 if (isFullyHidden()) {
1386 setClipBounds(null);
1387 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08001388 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001389 setClipBounds(mRequestedClipBounds);
1390 } else {
1391 setClipBounds(null);
1392 }
Lucas Dupin64e2f572019-03-21 14:21:14 -07001393
1394 setClipToOutline(clipToOutline);
Selim Cinek94c2d822016-07-13 18:50:04 -07001395 }
1396
1397 /**
1398 * @return The translation at the beginning when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001399 * Measured relative to the resting position.
Selim Cinek94c2d822016-07-13 18:50:04 -07001400 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001401 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001402 private float getExpandTranslationStart() {
Selim Cinek083f2142018-11-06 16:32:23 -08001403 return -mTopPadding + getMinExpansionHeight() - mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001404 }
1405
1406 /**
1407 * @return the position from where the appear transition starts when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001408 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001409 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001410 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001411 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001412 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001413 return mHeadsUpInset
1414 + getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -07001415 }
Selim Cinek48ff9b42016-11-09 19:31:51 -08001416 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001417 }
1418
1419 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001420 * @return the height of the top heads up notification when pinned. This is different from the
Jason Monke59dc402018-08-16 12:05:01 -04001421 * intrinsic height, which also includes whether the notification is system expanded and
1422 * is mainly used when dragging down from a heads up notification.
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001423 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001424 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001425 private int getTopHeadsUpPinnedHeight() {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001426 NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001427 if (topEntry == null) {
1428 return 0;
1429 }
Evan Laird94492852018-10-25 13:43:01 -04001430 ExpandableNotificationRow row = topEntry.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001431 if (row.isChildInGroup()) {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001432 final NotificationEntry groupSummary
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001433 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
1434 if (groupSummary != null) {
Evan Laird94492852018-10-25 13:43:01 -04001435 row = groupSummary.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001436 }
1437 }
1438 return row.getPinnedHeadsUpHeight();
1439 }
1440
1441 /**
Selim Cinek94c2d822016-07-13 18:50:04 -07001442 * @return the position from where the appear transition ends when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001443 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001444 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001445 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001446 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -07001447 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -07001448 int notGoneChildCount = getNotGoneChildCount();
Julia Reynolds34f14962018-05-03 12:40:20 +00001449 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001450 if (isHeadsUpTransition()
Selim Cinekc1d9ab22019-05-21 18:08:30 -07001451 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDozing())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001452 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +01001453 } else {
1454 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001455 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1456 appearPosition += mShelf.getIntrinsicHeight();
1457 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001458 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001459 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001460 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001461 }
1462 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001463 }
1464
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001465 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001466 private boolean isHeadsUpTransition() {
Gus Prevase2d6f042018-10-17 15:25:30 -04001467 NotificationSection firstVisibleSection = getFirstVisibleSection();
1468 return mTrackingHeadsUp && firstVisibleSection != null
Selim Cinek459aee32019-02-20 11:18:56 -08001469 && firstVisibleSection.getFirstVisibleChild().isAboveShelf();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001470 }
1471
Selim Cinek94c2d822016-07-13 18:50:04 -07001472 /**
1473 * @param height the height of the panel
1474 * @return the fraction of the appear animation that has been performed
1475 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001476 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekc7e4cb52019-06-20 15:41:45 -07001477 public float calculateAppearFraction(float height) {
Selim Cinek94c2d822016-07-13 18:50:04 -07001478 float appearEndPosition = getAppearEndPosition();
1479 float appearStartPosition = getAppearStartPosition();
1480 return (height - appearStartPosition)
1481 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001482 }
1483
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001484 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001485 public float getStackTranslation() {
1486 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001487 }
1488
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001489 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001490 private void setStackTranslation(float stackTranslation) {
1491 if (stackTranslation != mStackTranslation) {
1492 mStackTranslation = stackTranslation;
1493 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001494 requestChildrenUpdate();
1495 }
Selim Cinek67b22602014-03-10 15:40:16 +01001496 }
1497
1498 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001499 * 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 +01001500 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1501 *
1502 * @return either the layout height or the externally defined height, whichever is smaller
1503 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001504 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek343e6e22014-04-11 21:23:30 +02001505 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001506 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1507 }
1508
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001509 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek816c8e42015-11-19 12:00:45 -08001510 public int getFirstItemMinHeight() {
1511 final ExpandableView firstChild = getFirstChildNotGone();
1512 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001513 }
1514
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001515 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Jason Monk16ac3772016-02-10 15:39:21 -05001516 public void setQsContainer(ViewGroup qsContainer) {
1517 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001518 }
1519
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001520 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek684a4422015-04-15 16:18:39 -07001521 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001522 if (v instanceof ExpandableNotificationRow) {
1523 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001524 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001525 }
1526 return false;
1527 }
1528
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001529 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cineka59ecc32015-04-07 10:51:49 -07001530 private boolean isHeadsUp(View v) {
1531 if (v instanceof ExpandableNotificationRow) {
1532 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1533 return row.isHeadsUp();
1534 }
1535 return false;
1536 }
1537
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001538 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001539 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1540 getLocationOnScreen(mTempInt2);
1541 float localTouchY = touchY - mTempInt2[1];
1542
1543 ExpandableView closestChild = null;
1544 float minDist = Float.MAX_VALUE;
1545
1546 // find the view closest to the location, accounting for GONE views
1547 final int count = getChildCount();
1548 for (int childIdx = 0; childIdx < count; childIdx++) {
1549 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1550 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001551 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001552 continue;
1553 }
1554 float childTop = slidingChild.getTranslationY();
1555 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001556 float bottom = childTop + slidingChild.getActualHeight()
1557 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001558
1559 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1560 if (dist < minDist) {
1561 closestChild = slidingChild;
1562 minDist = dist;
1563 }
1564 }
1565 return closestChild;
1566 }
1567
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001568 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
1569 private ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001570 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1571
1572 }
1573
1574 /**
1575 * Get the child at a certain screen location.
1576 *
Jason Monke59dc402018-08-16 12:05:01 -04001577 * @param touchX the x coordinate
1578 * @param touchY the y coordinate
Selim Cinek34ed7c02017-09-08 15:03:12 -07001579 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1580 * @return the child at the given location.
1581 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001582 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek34ed7c02017-09-08 15:03:12 -07001583 private ExpandableView getChildAtPosition(float touchX, float touchY,
1584 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001585 // find the view under the pointer, accounting for GONE views
1586 final int count = getChildCount();
1587 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001588 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001589 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001590 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001591 continue;
1592 }
Selim Cinek89faff12014-06-19 16:29:04 -07001593 float childTop = slidingChild.getTranslationY();
1594 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001595 float bottom = childTop + slidingChild.getActualHeight()
1596 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001597
1598 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1599 // camera affordance).
1600 int left = 0;
1601 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001602
Selim Cinek34ed7c02017-09-08 15:03:12 -07001603 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001604 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001605 if (slidingChild instanceof ExpandableNotificationRow) {
1606 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Ned Burnsf81c4c42019-01-07 14:10:43 -05001607 NotificationEntry entry = row.getEntry();
Selim Cinek131c1e22015-05-11 19:04:49 -07001608 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Evan Laird94492852018-10-25 13:43:01 -04001609 && mHeadsUpManager.getTopEntry().getRow() != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001610 && mGroupManager.getGroupSummary(
Evan Laird94492852018-10-25 13:43:01 -04001611 mHeadsUpManager.getTopEntry().notification)
1612 != entry) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001613 continue;
1614 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001615 return row.getViewAtPosition(touchY - childTop);
1616 }
Selim Cinek67b22602014-03-10 15:40:16 +01001617 return slidingChild;
1618 }
1619 }
1620 return null;
1621 }
1622
Selim Cinek3d6ae232019-01-04 14:14:33 -08001623 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001624 getLocationOnScreen(mTempInt2);
1625 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001626 }
1627
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001628 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001629 public void setScrollingEnabled(boolean enable) {
1630 mScrollingEnabled = enable;
1631 }
1632
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001633 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001634 public void lockScrollTo(View v) {
1635 if (mForcedScroll == v) {
1636 return;
1637 }
1638 mForcedScroll = v;
1639 scrollTo(v);
1640 }
1641
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001642 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001643 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001644 ExpandableView expandableView = (ExpandableView) v;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001645 if (ANCHOR_SCROLLING) {
1646 // TODO
1647 } else {
1648 int positionInLinearLayout = getPositionInLinearLayout(v);
1649 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1650 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001651
Gus Prevas0fa58d62019-01-11 13:58:40 -05001652 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1653 // that it is not visible anymore.
1654 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1655 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1656 mDontReportNextOverScroll = true;
1657 animateScroll();
1658 return true;
1659 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001660 }
Ricky Waicd35def2016-05-03 11:07:07 +01001661 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001662 }
1663
Adrian Roos181385c2016-05-05 17:45:44 -04001664 /**
1665 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
Jason Monke59dc402018-08-16 12:05:01 -04001666 * the IME.
Adrian Roos181385c2016-05-05 17:45:44 -04001667 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001668 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos181385c2016-05-05 17:45:44 -04001669 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1670 return positionInLinearLayout + v.getIntrinsicHeight() +
felkachang529bfe62018-07-04 12:51:44 +08001671 getImeInset() - getHeight()
1672 + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
Adrian Roos181385c2016-05-05 17:45:44 -04001673 }
1674
Adrian Roos5153d4a2016-03-22 10:01:56 -07001675 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001676 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001677 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001678 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001679
Gus Prevas0fa58d62019-01-11 13:58:40 -05001680 if (ANCHOR_SCROLLING) {
1681 // TODO
1682 } else {
1683 int range = getScrollRange();
1684 if (mOwnScrollY > range) {
1685 // HACK: We're repeatedly getting staggered insets here while the IME is
1686 // animating away. To work around that we'll wait until things have settled.
1687 removeCallbacks(mReclamp);
1688 postDelayed(mReclamp, 50);
1689 } else if (mForcedScroll != null) {
1690 // The scroll was requested before we got the actual inset - in case we need
1691 // to scroll up some more do so now.
1692 scrollTo(mForcedScroll);
1693 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001694 }
1695 return insets;
1696 }
1697
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001698 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001699 private Runnable mReclamp = new Runnable() {
1700 @Override
1701 public void run() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001702 if (ANCHOR_SCROLLING) {
1703 // TODO
1704 } else {
1705 int range = getScrollRange();
1706 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1707 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001708 mDontReportNextOverScroll = true;
1709 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001710 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001711 }
1712 };
1713
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001714 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
1715 public void setExpandingEnabled(boolean enable) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001716 mExpandHelper.setEnabled(enable);
1717 }
1718
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001719 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001720 private boolean isScrollingEnabled() {
1721 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001722 }
1723
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001724 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07001725 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001726 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001727 }
1728
Selim Cinek67b22602014-03-10 15:40:16 +01001729 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001730 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001731 protected void onConfigurationChanged(Configuration newConfig) {
1732 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001733 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001734 float densityScale = getResources().getDisplayMetrics().density;
1735 mSwipeHelper.setDensityScale(densityScale);
1736 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1737 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1738 initView(getContext());
1739 }
1740
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001741 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dan Sandlereceda3d2014-07-21 15:35:01 -04001742 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001743 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1744 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001745 }
1746
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001747 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001748 private void snapViewIfNeeded(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001749 ExpandableNotificationRow child = entry.getRow();
dongwan0605.kim30637e42016-03-02 17:16:47 +09001750 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001751 // If the child is showing the notification menu snap to that
Evan Lairde55c6012019-03-13 12:54:37 -04001752 if (child.getProvider() != null) {
1753 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
1754 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
1755 }
dongwan0605.kim30637e42016-03-02 17:16:47 +09001756 }
1757
Selim Cinek67b22602014-03-10 15:40:16 +01001758 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001759 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001760 public ViewGroup getViewParentForNotification(NotificationEntry entry) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001761 return this;
1762 }
1763
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001764 /**
1765 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1766 *
1767 * @param deltaY The amount to scroll upwards, has to be positive.
1768 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001769 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001770 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001771 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001772 private float overScrollUp(int deltaY, int range) {
1773 deltaY = Math.max(deltaY, 0);
1774 float currentTopAmount = getCurrentOverScrollAmount(true);
1775 float newTopAmount = currentTopAmount - deltaY;
1776 if (currentTopAmount > 0) {
1777 setOverScrollAmount(newTopAmount, true /* onTop */,
1778 false /* animate */);
1779 }
1780 // Top overScroll might not grab all scrolling motion,
1781 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001782 if (ANCHOR_SCROLLING) {
1783 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1784 // TODO: once we're recycling this will need to check the adapter position of the child
1785 ExpandableView lastRow = getLastRowNotGone();
Gus Prevascdc98342019-01-14 14:29:44 -05001786 if (lastRow != null && !lastRow.isInShelf()) {
1787 float distanceToMax = Math.max(0, getMaxPositiveScrollAmount());
1788 if (scrollAmount > distanceToMax) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001789 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1790 // We overScroll on the bottom
1791 setOverScrolledPixels(currentBottomPixels + (scrollAmount - distanceToMax),
1792 false /* onTop */,
1793 false /* animate */);
1794 mScrollAnchorViewY -= distanceToMax;
1795 scrollAmount = 0f;
1796 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001797 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001798 return scrollAmount;
1799 } else {
1800 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1801 float newScrollY = mOwnScrollY + scrollAmount;
1802 if (newScrollY > range) {
1803 if (!mExpandedInThisMotion) {
1804 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1805 // We overScroll on the bottom
1806 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1807 false /* onTop */,
1808 false /* animate */);
1809 }
1810 setOwnScrollY(range);
1811 scrollAmount = 0.0f;
1812 }
1813 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001814 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001815 }
1816
1817 /**
1818 * Perform a scroll downward and adapt the overscroll amounts accordingly
1819 *
1820 * @param deltaY The amount to scroll downwards, has to be negative.
1821 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001822 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001823 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001824 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001825 private float overScrollDown(int deltaY) {
1826 deltaY = Math.min(deltaY, 0);
1827 float currentBottomAmount = getCurrentOverScrollAmount(false);
1828 float newBottomAmount = currentBottomAmount + deltaY;
1829 if (currentBottomAmount > 0) {
1830 setOverScrollAmount(newBottomAmount, false /* onTop */,
1831 false /* animate */);
1832 }
1833 // Bottom overScroll might not grab all scrolling motion,
1834 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001835 if (ANCHOR_SCROLLING) {
1836 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1837 // TODO: once we're recycling this will need to check the adapter position of the child
1838 ExpandableView firstChild = getFirstChildNotGone();
1839 float top = firstChild.getTranslationY();
1840 float distanceToTop = mScrollAnchorView.getTranslationY() - top - mScrollAnchorViewY;
1841 if (distanceToTop < -scrollAmount) {
1842 float currentTopPixels = getCurrentOverScrolledPixels(true);
1843 // We overScroll on the top
1844 setOverScrolledPixels(currentTopPixels + (-scrollAmount - distanceToTop),
1845 true /* onTop */,
1846 false /* animate */);
1847 mScrollAnchorView = firstChild;
1848 mScrollAnchorViewY = 0;
1849 scrollAmount = 0f;
1850 }
1851 return scrollAmount;
1852 } else {
1853 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1854 float newScrollY = mOwnScrollY + scrollAmount;
1855 if (newScrollY < 0) {
1856 float currentTopPixels = getCurrentOverScrolledPixels(true);
1857 // We overScroll on the top
1858 setOverScrolledPixels(currentTopPixels - newScrollY,
1859 true /* onTop */,
1860 false /* animate */);
1861 setOwnScrollY(0);
1862 scrollAmount = 0.0f;
1863 }
1864 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001865 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001866 }
1867
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001868 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001869 private void initVelocityTrackerIfNotExists() {
1870 if (mVelocityTracker == null) {
1871 mVelocityTracker = VelocityTracker.obtain();
1872 }
1873 }
1874
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001875 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001876 private void recycleVelocityTracker() {
1877 if (mVelocityTracker != null) {
1878 mVelocityTracker.recycle();
1879 mVelocityTracker = null;
1880 }
1881 }
1882
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001883 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001884 private void initOrResetVelocityTracker() {
1885 if (mVelocityTracker == null) {
1886 mVelocityTracker = VelocityTracker.obtain();
1887 } else {
1888 mVelocityTracker.clear();
1889 }
1890 }
1891
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001892 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001893 public void setFinishScrollingCallback(Runnable runnable) {
1894 mFinishScrollingCallback = runnable;
1895 }
1896
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001897 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9212de82017-02-06 16:04:28 -08001898 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001899 if (mScroller.computeScrollOffset()) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001900 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001901 int oldY = mLastScrollerY;
1902 int y = mScroller.getCurrY();
1903 int deltaY = y - oldY;
1904 if (deltaY != 0) {
1905 int maxNegativeScrollAmount = getMaxNegativeScrollAmount();
1906 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
1907 if ((maxNegativeScrollAmount < 0 && deltaY < maxNegativeScrollAmount)
1908 || (maxPositiveScrollAmount > 0 && deltaY > maxPositiveScrollAmount)) {
1909 // This frame takes us into overscroll, so set the max overscroll based on
1910 // the current velocity
1911 setMaxOverScrollFromCurrentVelocity();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001912 }
Gus Prevascdc98342019-01-14 14:29:44 -05001913 customOverScrollBy(deltaY, oldY, 0, (int) mMaxOverScroll);
1914 mLastScrollerY = y;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001915 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001916 } else {
1917 int oldY = mOwnScrollY;
1918 int y = mScroller.getCurrY();
Selim Cinek67b22602014-03-10 15:40:16 +01001919
Gus Prevas0fa58d62019-01-11 13:58:40 -05001920 if (oldY != y) {
1921 int range = getScrollRange();
1922 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
Gus Prevascdc98342019-01-14 14:29:44 -05001923 // This frame takes us into overscroll, so set the max overscroll based on
1924 // the current velocity
1925 setMaxOverScrollFromCurrentVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +01001926 }
Selim Cinek67b22602014-03-10 15:40:16 +01001927
Gus Prevas0fa58d62019-01-11 13:58:40 -05001928 if (mDontClampNextScroll) {
1929 range = Math.max(range, oldY);
1930 }
1931 customOverScrollBy(y - oldY, oldY, range,
1932 (int) (mMaxOverScroll));
Adrian Roos5153d4a2016-03-22 10:01:56 -07001933 }
Selim Cinek67b22602014-03-10 15:40:16 +01001934 }
1935
Gus Prevascdc98342019-01-14 14:29:44 -05001936 postOnAnimation(mReflingAndAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001937 } else {
1938 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001939 if (mFinishScrollingCallback != null) {
1940 mFinishScrollingCallback.run();
1941 }
Selim Cinek67b22602014-03-10 15:40:16 +01001942 }
1943 }
1944
Gus Prevascdc98342019-01-14 14:29:44 -05001945 private void setMaxOverScrollFromCurrentVelocity() {
1946 float currVelocity = mScroller.getCurrVelocity();
1947 if (currVelocity >= mMinimumVelocity) {
1948 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
Selim Cinek4195dd02014-05-19 18:16:14 +02001949 }
Gus Prevascdc98342019-01-14 14:29:44 -05001950 }
Selim Cinek4195dd02014-05-19 18:16:14 +02001951
Gus Prevascdc98342019-01-14 14:29:44 -05001952 /**
1953 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
1954 * would cause us to exceed the provided maximum overscroll, springs back instead.
1955 *
1956 * This method performs the determination of whether we're exceeding the overscroll and clamps
1957 * the scroll amount if so. The actual scrolling/overscrolling happens in
1958 * {@link #onCustomOverScrolled(int, boolean)} (absolute scrolling) or
1959 * {@link #onCustomOverScrolledBy(int, boolean)} (anchor scrolling).
1960 *
1961 * @param deltaY The (signed) number of pixels to scroll.
1962 * @param scrollY The current scroll position (absolute scrolling only).
1963 * @param scrollRangeY The maximum allowable scroll position (absolute scrolling only).
1964 * @param maxOverScrollY The current (unsigned) limit on number of pixels to overscroll by.
1965 */
Selim Cinek4195dd02014-05-19 18:16:14 +02001966 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevascdc98342019-01-14 14:29:44 -05001967 private void customOverScrollBy(int deltaY, int scrollY, int scrollRangeY, int maxOverScrollY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001968 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001969 boolean clampedY = false;
1970 if (deltaY < 0) {
1971 int maxScrollAmount = getMaxNegativeScrollAmount();
1972 if (maxScrollAmount > Integer.MIN_VALUE) {
1973 maxScrollAmount -= maxOverScrollY;
1974 if (deltaY < maxScrollAmount) {
1975 deltaY = maxScrollAmount;
1976 clampedY = true;
1977 }
1978 }
1979 } else {
1980 int maxScrollAmount = getMaxPositiveScrollAmount();
1981 if (maxScrollAmount < Integer.MAX_VALUE) {
1982 maxScrollAmount += maxOverScrollY;
1983 if (deltaY > maxScrollAmount) {
1984 deltaY = maxScrollAmount;
1985 clampedY = true;
1986 }
1987 }
1988 }
1989 onCustomOverScrolledBy(deltaY, clampedY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05001990 } else {
1991 int newScrollY = scrollY + deltaY;
1992 final int top = -maxOverScrollY;
1993 final int bottom = maxOverScrollY + scrollRangeY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001994
Gus Prevas0fa58d62019-01-11 13:58:40 -05001995 boolean clampedY = false;
1996 if (newScrollY > bottom) {
1997 newScrollY = bottom;
1998 clampedY = true;
1999 } else if (newScrollY < top) {
2000 newScrollY = top;
2001 clampedY = true;
2002 }
Selim Cinek4195dd02014-05-19 18:16:14 +02002003
Gus Prevas0fa58d62019-01-11 13:58:40 -05002004 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02002005 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002006 }
2007
2008 /**
2009 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
2010 * overscroll effect based on numPixels. By default this will also cancel animations on the
2011 * same overScroll edge.
2012 *
2013 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
2014 * the rubber-banding logic.
Jason Monke59dc402018-08-16 12:05:01 -04002015 * @param onTop Should the effect be applied on top of the scroller.
2016 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002017 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002018 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002019 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07002020 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002021 }
2022
2023 /**
2024 * Set the effective overScroll amount which will be directly reflected in the layout.
2025 * By default this will also cancel animations on the same overScroll edge.
2026 *
Jason Monke59dc402018-08-16 12:05:01 -04002027 * @param amount The amount to overScroll by.
2028 * @param onTop Should the effect be applied on top of the scroller.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002029 * @param animate Should an animation be performed.
2030 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002031
2032 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002033 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
2034 setOverScrollAmount(amount, onTop, animate, true);
2035 }
2036
2037 /**
2038 * Set the effective overScroll amount which will be directly reflected in the layout.
2039 *
Jason Monke59dc402018-08-16 12:05:01 -04002040 * @param amount The amount to overScroll by.
2041 * @param onTop Should the effect be applied on top of the scroller.
2042 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002043 * @param cancelAnimators Should running animations be cancelled.
2044 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002045 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002046 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
2047 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002048 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
2049 }
2050
2051 /**
2052 * Set the effective overScroll amount which will be directly reflected in the layout.
2053 *
Jason Monke59dc402018-08-16 12:05:01 -04002054 * @param amount The amount to overScroll by.
2055 * @param onTop Should the effect be applied on top of the scroller.
2056 * @param animate Should an animation be performed.
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002057 * @param cancelAnimators Should running animations be cancelled.
Jason Monke59dc402018-08-16 12:05:01 -04002058 * @param isRubberbanded The value which will be passed to
2059 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002060 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002061 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002062 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
2063 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002064 if (cancelAnimators) {
2065 mStateAnimator.cancelOverScrollAnimators(onTop);
2066 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002067 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002068 }
2069
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002070 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002071 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
2072 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002073 amount = Math.max(0, amount);
2074 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002075 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002076 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07002077 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002078 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002079 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002080 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002081 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002082 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002083 }
2084 }
2085
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002086 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002087 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002088 mExpandHelper.onlyObserveMovements(amount > 1.0f);
2089 if (mDontReportNextOverScroll) {
2090 mDontReportNextOverScroll = false;
2091 return;
2092 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02002093 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002094 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002095 }
2096 }
2097
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002098 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi290600a2014-05-30 17:02:20 +02002099 public void setOverscrollTopChangedListener(
2100 OnOverscrollTopChangedListener overscrollTopChangedListener) {
2101 mOverscrollTopChangedListener = overscrollTopChangedListener;
2102 }
2103
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002104 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002105 public float getCurrentOverScrollAmount(boolean top) {
2106 return mAmbientState.getOverScrollAmount(top);
2107 }
2108
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002109 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002110 public float getCurrentOverScrolledPixels(boolean top) {
Jason Monke59dc402018-08-16 12:05:01 -04002111 return top ? mOverScrolledTopPixels : mOverScrolledBottomPixels;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002112 }
2113
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002114 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002115 private void setOverScrolledPixels(float amount, boolean onTop) {
2116 if (onTop) {
2117 mOverScrolledTopPixels = amount;
2118 } else {
2119 mOverScrolledBottomPixels = amount;
2120 }
2121 }
2122
Gus Prevascdc98342019-01-14 14:29:44 -05002123 /**
2124 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
2125 * would cause us to exceed the provided maximum overscroll, springs back instead.
2126 *
2127 * @param deltaY The (signed) number of pixels to scroll.
2128 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2129 * the overscroll limit.
2130 */
2131 private void onCustomOverScrolledBy(int deltaY, boolean clampedY) {
2132 assert ANCHOR_SCROLLING;
Gus Prevas0fa58d62019-01-11 13:58:40 -05002133 mScrollAnchorViewY -= deltaY;
2134 // Treat animating scrolls differently; see #computeScroll() for why.
2135 if (!mScroller.isFinished()) {
Gus Prevascdc98342019-01-14 14:29:44 -05002136 if (clampedY) {
2137 springBack();
2138 } else {
2139 float overScrollTop = getCurrentOverScrollAmount(true /* top */);
2140 if (isScrolledToTop() && mScrollAnchorViewY > 0) {
2141 notifyOverscrollTopListener(mScrollAnchorViewY,
2142 isRubberbanded(true /* onTop */));
2143 } else {
2144 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true /* onTop */));
2145 }
2146 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002147 }
2148 updateScrollAnchor();
2149 updateOnScrollChange();
2150 }
2151
Gus Prevascdc98342019-01-14 14:29:44 -05002152 /**
2153 * Scrolls to the given position, overscrolling if needed. If called during a fling and the
2154 * position exceeds the provided maximum overscroll, springs back instead.
2155 *
2156 * @param scrollY The target scroll position.
2157 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2158 * the overscroll limit.
2159 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002160 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek9212de82017-02-06 16:04:28 -08002161 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002162 assert !ANCHOR_SCROLLING;
Selim Cinek67b22602014-03-10 15:40:16 +01002163 // Treat animating scrolls differently; see #computeScroll() for why.
2164 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07002165 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002166 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002167 springBack();
2168 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02002169 float overScrollTop = getCurrentOverScrollAmount(true);
2170 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002171 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002172 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002173 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002174 }
Selim Cinek67b22602014-03-10 15:40:16 +01002175 }
Selim Cinek67b22602014-03-10 15:40:16 +01002176 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08002177 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002178 }
2179 }
2180
Gus Prevascdc98342019-01-14 14:29:44 -05002181 /**
2182 * Springs back from an overscroll by stopping the {@link #mScroller} and animating the
2183 * overscroll amount back to zero.
2184 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002185 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002186 private void springBack() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002187 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002188 boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0;
2189 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
2190 boolean overscrolledBottom = maxPositiveScrollAmount < 0;
2191 if (overScrolledTop || overscrolledBottom) {
2192 float newAmount;
2193 if (overScrolledTop) {
2194 newAmount = mScrollAnchorViewY;
2195 mScrollAnchorViewY = 0;
2196 mDontReportNextOverScroll = true;
2197 } else {
2198 newAmount = -maxPositiveScrollAmount;
2199 mScrollAnchorViewY -= maxPositiveScrollAmount;
2200 }
2201 setOverScrollAmount(newAmount, overScrolledTop, false);
2202 setOverScrollAmount(0.0f, overScrolledTop, true);
2203 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002204 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002205 } else {
2206 int scrollRange = getScrollRange();
2207 boolean overScrolledTop = mOwnScrollY <= 0;
2208 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2209 if (overScrolledTop || overScrolledBottom) {
2210 boolean onTop;
2211 float newAmount;
2212 if (overScrolledTop) {
2213 onTop = true;
2214 newAmount = -mOwnScrollY;
2215 setOwnScrollY(0);
2216 mDontReportNextOverScroll = true;
2217 } else {
2218 onTop = false;
2219 newAmount = mOwnScrollY - scrollRange;
2220 setOwnScrollY(scrollRange);
2221 }
2222 setOverScrollAmount(newAmount, onTop, false);
2223 setOverScrollAmount(0.0f, onTop, true);
2224 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002225 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002226 }
2227 }
2228
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002229 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek67b22602014-03-10 15:40:16 +01002230 private int getScrollRange() {
felkachang529bfe62018-07-04 12:51:44 +08002231 // In current design, it only use the top HUN to treat all of HUNs
2232 // although there are more than one HUNs
2233 int contentHeight = mContentHeight;
2234 if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
2235 contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
2236 }
2237 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002238 int imeInset = getImeInset();
felkachang529bfe62018-07-04 12:51:44 +08002239 scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
Selim Cineka424c502016-04-05 13:07:54 -07002240 return scrollRange;
2241 }
2242
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002243 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cineka424c502016-04-05 13:07:54 -07002244 private int getImeInset() {
2245 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002246 }
2247
Selim Cinek343e6e22014-04-11 21:23:30 +02002248 /**
2249 * @return the first child which has visibility unequal to GONE
2250 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002251 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb55386d2015-12-16 17:26:49 -08002252 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002253 int childCount = getChildCount();
2254 for (int i = 0; i < childCount; i++) {
2255 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002256 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002257 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002258 }
2259 }
2260 return null;
2261 }
2262
Selim Cinek4a1ac842014-05-01 15:51:58 +02002263 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002264 * @return the child before the given view which has visibility unequal to GONE
2265 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002266 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002267 public ExpandableView getViewBeforeView(ExpandableView view) {
2268 ExpandableView previousView = null;
2269 int childCount = getChildCount();
2270 for (int i = 0; i < childCount; i++) {
2271 View child = getChildAt(i);
2272 if (child == view) {
2273 return previousView;
2274 }
2275 if (child.getVisibility() != View.GONE) {
2276 previousView = (ExpandableView) child;
2277 }
2278 }
2279 return null;
2280 }
2281
2282 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002283 * @return The first child which has visibility unequal to GONE which is currently below the
Jason Monke59dc402018-08-16 12:05:01 -04002284 * given translationY or equal to it.
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002285 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002286 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekef8c2252017-02-10 14:52:18 -08002287 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002288 int childCount = getChildCount();
2289 for (int i = 0; i < childCount; i++) {
2290 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002291 if (child.getVisibility() == View.GONE) {
2292 continue;
2293 }
2294 float rowTranslation = child.getTranslationY();
2295 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002296 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002297 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2298 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2299 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2300 List<ExpandableNotificationRow> notificationChildren =
2301 row.getNotificationChildren();
2302 for (int childIndex = 0; childIndex < notificationChildren.size();
2303 childIndex++) {
2304 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2305 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2306 return rowChild;
2307 }
2308 }
2309 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002310 }
2311 }
2312 return null;
2313 }
2314
2315 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002316 * @return the last child which has visibility unequal to GONE
2317 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002318 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002319 public ExpandableView getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002320 int childCount = getChildCount();
2321 for (int i = childCount - 1; i >= 0; i--) {
2322 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002323 if (child.getVisibility() != View.GONE && child != mShelf) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002324 return (ExpandableView) child;
Selim Cinek4a1ac842014-05-01 15:51:58 +02002325 }
2326 }
2327 return null;
2328 }
2329
Gus Prevas0fa58d62019-01-11 13:58:40 -05002330 private ExpandableNotificationRow getLastRowNotGone() {
2331 int childCount = getChildCount();
2332 for (int i = childCount - 1; i >= 0; i--) {
2333 View child = getChildAt(i);
2334 if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
2335 return (ExpandableNotificationRow) child;
2336 }
2337 }
2338 return null;
2339 }
2340
Jorim Jaggi069cd032014-05-15 03:09:01 +02002341 /**
2342 * @return the number of children which have visibility unequal to GONE
2343 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002344 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi069cd032014-05-15 03:09:01 +02002345 public int getNotGoneChildCount() {
2346 int childCount = getChildCount();
2347 int count = 0;
2348 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002349 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002350 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002351 count++;
2352 }
2353 }
2354 return count;
2355 }
2356
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002357 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01002358 private void updateContentHeight() {
2359 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002360 float previousPaddingRequest = mPaddingBetweenElements;
2361 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002362 int numShownItems = 0;
2363 boolean finish = false;
Selim Cinek5040f2e2019-02-14 18:22:42 -08002364 int maxDisplayedNotifications = mMaxDisplayedNotifications;
Adrian Roos7a9551a2017-01-11 12:27:49 -08002365
Selim Cinek67b22602014-03-10 15:40:16 +01002366 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002367 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002368 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002369 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002370 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002371 boolean limitReached = maxDisplayedNotifications != -1
2372 && numShownItems >= maxDisplayedNotifications;
Selim Cinek5040f2e2019-02-14 18:22:42 -08002373 if (limitReached) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002374 expandableView = mShelf;
2375 finish = true;
2376 }
Selim Cinek42357e02016-02-24 18:48:01 -08002377 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002378 float padding;
2379 if (increasedPaddingAmount >= 0.0f) {
2380 padding = (int) NotificationUtils.interpolate(
2381 previousPaddingRequest,
2382 mIncreasedPaddingBetweenElements,
2383 increasedPaddingAmount);
2384 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002385 mPaddingBetweenElements,
2386 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002387 increasedPaddingAmount);
2388 } else {
2389 int ownPadding = (int) NotificationUtils.interpolate(
2390 0,
2391 mPaddingBetweenElements,
2392 1.0f + increasedPaddingAmount);
2393 if (previousPaddingAmount > 0.0f) {
2394 padding = (int) NotificationUtils.interpolate(
2395 ownPadding,
2396 mIncreasedPaddingBetweenElements,
2397 previousPaddingAmount);
2398 } else {
2399 padding = ownPadding;
2400 }
2401 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002402 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002403 if (height != 0) {
2404 height += padding;
2405 }
2406 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002407 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002408 numShownItems++;
2409 if (finish) {
2410 break;
2411 }
Selim Cinek67b22602014-03-10 15:40:16 +01002412 }
2413 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002414 mIntrinsicContentHeight = height;
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002415
Selim Cinekdb6d6df2019-05-22 20:39:59 -07002416 // The topPadding can be bigger than the regular padding when qs is expanded, in that
2417 // state the maxPanelHeight and the contentHeight should be bigger
2418 mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002419 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002420 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002421 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002422 }
2423
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002424 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002425 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbe2c4432017-05-30 12:11:09 -07002426 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002427 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002428 }
2429
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002430 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07002431 private void updateScrollability() {
Riddle Hsu065c01c2018-05-10 23:14:19 +08002432 boolean scrollable = !mQsExpanded && getScrollRange() > 0;
Selim Cinekc22fff62016-05-20 12:44:30 -07002433 if (scrollable != mScrollable) {
2434 mScrollable = scrollable;
2435 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002436 updateForwardAndBackwardScrollability();
2437 }
2438 }
2439
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002440 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef406062016-09-29 17:33:13 -07002441 private void updateForwardAndBackwardScrollability() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002442 boolean forwardScrollable = mScrollable && !isScrolledToBottom();
2443 boolean backwardsScrollable = mScrollable && !isScrolledToTop();
Selim Cinekef406062016-09-29 17:33:13 -07002444 boolean changed = forwardScrollable != mForwardScrollable
2445 || backwardsScrollable != mBackwardScrollable;
2446 mForwardScrollable = forwardScrollable;
2447 mBackwardScrollable = backwardsScrollable;
2448 if (changed) {
2449 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002450 }
Selim Cinek67b22602014-03-10 15:40:16 +01002451 }
2452
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002453 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002454 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002455 // No need to update the background color if it's not being drawn.
Selim Cinek195dfc52019-05-30 19:35:05 -07002456 if (!mShouldDrawNotificationBackground) {
Selim Cinek6811d722016-01-19 17:53:12 -08002457 return;
2458 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002459
Selim Cinek6811d722016-01-19 17:53:12 -08002460 updateBackgroundBounds();
Gus Prevase2d6f042018-10-17 15:25:30 -04002461 if (didSectionBoundsChange()) {
2462 boolean animate = mAnimateNextSectionBoundsChange || mAnimateNextBackgroundTop
2463 || mAnimateNextBackgroundBottom || areSectionBoundsAnimating();
Selim Cinek54680902016-10-19 16:49:44 -07002464 if (!isExpanded()) {
2465 abortBackgroundAnimators();
2466 animate = false;
2467 }
2468 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002469 startBackgroundAnimation();
2470 } else {
Gus Prevase2d6f042018-10-17 15:25:30 -04002471 for (NotificationSection section : mSections) {
2472 section.resetCurrentBounds();
2473 }
Lucas Dupin90a38dd2018-09-05 09:37:37 -07002474 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002475 }
2476 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002477 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002478 }
Selim Cinek614576e2016-01-20 10:54:09 -08002479 mAnimateNextBackgroundTop = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04002480 mAnimateNextBackgroundBottom = false;
2481 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08002482 }
2483
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002484 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek54680902016-10-19 16:49:44 -07002485 private void abortBackgroundAnimators() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002486 for (NotificationSection section : mSections) {
2487 section.cancelAnimators();
Selim Cinek54680902016-10-19 16:49:44 -07002488 }
2489 }
2490
Gus Prevase2d6f042018-10-17 15:25:30 -04002491 private boolean didSectionBoundsChange() {
2492 for (NotificationSection section : mSections) {
2493 if (section.didBoundsChange()) {
2494 return true;
2495 }
2496 }
2497 return false;
2498 }
2499
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002500 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevase2d6f042018-10-17 15:25:30 -04002501 private boolean areSectionBoundsAnimating() {
2502 for (NotificationSection section : mSections) {
2503 if (section.areBoundsAnimating()) {
2504 return true;
2505 }
2506 }
2507 return false;
Selim Cinek614576e2016-01-20 10:54:09 -08002508 }
2509
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002510 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08002511 private void startBackgroundAnimation() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002512 // TODO(kprevas): do we still need separate fields for top/bottom?
2513 // or can each section manage its own animation state?
2514 NotificationSection firstVisibleSection = getFirstVisibleSection();
2515 NotificationSection lastVisibleSection = getLastVisibleSection();
2516 for (NotificationSection section : mSections) {
2517 section.startBackgroundAnimation(
2518 section == firstVisibleSection
2519 ? mAnimateNextBackgroundTop
2520 : mAnimateNextSectionBoundsChange,
2521 section == lastVisibleSection
2522 ? mAnimateNextBackgroundBottom
2523 : mAnimateNextSectionBoundsChange);
Selim Cinek614576e2016-01-20 10:54:09 -08002524 }
Selim Cinek6811d722016-01-19 17:53:12 -08002525 }
2526
2527 /**
2528 * Update the background bounds to the new desired bounds
2529 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002530 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002531 private void updateBackgroundBounds() {
shawnlin27716722019-04-17 20:09:45 +08002532 int left = mSidePaddings;
2533 int right = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -04002534 for (NotificationSection section : mSections) {
2535 section.getBounds().left = left;
2536 section.getBounds().right = right;
2537 }
shawnlin3a2a2e22018-05-04 17:09:50 +08002538
Selim Cinek614576e2016-01-20 10:54:09 -08002539 if (!mIsExpanded) {
Gus Prevase2d6f042018-10-17 15:25:30 -04002540 for (NotificationSection section : mSections) {
2541 section.getBounds().top = 0;
2542 section.getBounds().bottom = 0;
2543 }
Selim Cinek1791f502016-10-07 17:38:03 -04002544 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002545 }
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002546 int minTopPosition = 0;
Gus Prevase2d6f042018-10-17 15:25:30 -04002547 NotificationSection lastSection = getLastVisibleSection();
Selim Cinekc3fec682019-06-06 18:11:07 -07002548 boolean onKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
2549 if (!onKeyguard) {
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002550 minTopPosition = (int) (mTopPadding + mStackTranslation);
2551 } else if (lastSection == null) {
2552 minTopPosition = mTopPadding;
Selim Cinek3776fe02016-02-04 13:32:43 -08002553 }
Selim Cinekc3fec682019-06-06 18:11:07 -07002554 boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1
2555 && (mAmbientState.isDozing()
2556 || (mKeyguardBypassController.getBypassEnabled() && onKeyguard));
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002557 for (NotificationSection section : mSections) {
2558 int minBottomPosition = minTopPosition;
2559 if (section == lastSection) {
2560 // We need to make sure the section goes all the way to the shelf
Selim Cinek3a1d2742019-03-11 18:38:29 -07002561 minBottomPosition = (int) (ViewState.getFinalTranslationY(mShelf)
2562 + mShelf.getIntrinsicHeight());
Gus Prevase83700cb2018-12-14 11:42:51 -05002563 }
Selim Cinekae55d832019-02-22 17:43:43 -08002564 minTopPosition = section.updateBounds(minTopPosition, minBottomPosition,
2565 shiftPulsingWithFirst);
2566 shiftPulsingWithFirst = false;
Gus Prevase83700cb2018-12-14 11:42:51 -05002567 }
Selim Cinek614576e2016-01-20 10:54:09 -08002568 }
2569
Gus Prevase2d6f042018-10-17 15:25:30 -04002570 private NotificationSection getFirstVisibleSection() {
2571 for (NotificationSection section : mSections) {
2572 if (section.getFirstVisibleChild() != null) {
2573 return section;
2574 }
2575 }
2576 return null;
2577 }
2578
2579 private NotificationSection getLastVisibleSection() {
2580 for (int i = mSections.length - 1; i >= 0; i--) {
2581 NotificationSection section = mSections[i];
2582 if (section.getLastVisibleChild() != null) {
2583 return section;
2584 }
2585 }
2586 return null;
2587 }
2588
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002589 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002590 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002591 int childCount = getChildCount();
2592 for (int i = childCount - 1; i >= 0; i--) {
2593 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002594 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2595 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002596 return (ActivatableNotificationView) child;
2597 }
2598 }
2599 return null;
2600 }
2601
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002602 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002603 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002604 int childCount = getChildCount();
2605 for (int i = 0; i < childCount; i++) {
2606 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002607 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2608 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002609 return (ActivatableNotificationView) child;
2610 }
2611 }
2612 return null;
2613 }
2614
Selim Cinek67b22602014-03-10 15:40:16 +01002615 /**
2616 * Fling the scroll view
2617 *
2618 * @param velocityY The initial velocity in the Y direction. Positive
2619 * numbers mean that the finger/cursor is moving down the screen,
2620 * which means we want to scroll towards the top.
2621 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002622 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Muyuan Li26e30ae2016-04-11 17:31:42 -07002623 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002624 if (getChildCount() > 0) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002625 float topAmount = getCurrentOverScrollAmount(true);
2626 float bottomAmount = getCurrentOverScrollAmount(false);
2627 if (velocityY < 0 && topAmount > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002628 if (ANCHOR_SCROLLING) {
2629 mScrollAnchorViewY += topAmount;
2630 } else {
2631 setOwnScrollY(mOwnScrollY - (int) topAmount);
2632 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002633 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002634 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002635 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002636 * mOverflingDistance + topAmount;
2637 } else if (velocityY > 0 && bottomAmount > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002638 if (ANCHOR_SCROLLING) {
2639 mScrollAnchorViewY -= bottomAmount;
2640 } else {
2641 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
2642 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002643 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002644 mMaxOverScroll = Math.abs(velocityY) / 1000f
2645 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
Jason Monke59dc402018-08-16 12:05:01 -04002646 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002647 } else {
2648 // it will be set once we reach the boundary
2649 mMaxOverScroll = 0.0f;
2650 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002651 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002652 flingScroller(velocityY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05002653 } else {
2654 int scrollRange = getScrollRange();
2655 int minScrollY = Math.max(0, scrollRange);
2656 if (mExpandedInThisMotion) {
2657 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2658 }
2659 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2660 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek94ab18c2016-02-25 12:35:51 -08002661 }
Selim Cinek67b22602014-03-10 15:40:16 +01002662
Selim Cinek9212de82017-02-06 16:04:28 -08002663 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002664 }
2665 }
2666
Selim Cinek1408eb52014-06-02 14:45:38 +02002667 /**
Gus Prevascdc98342019-01-14 14:29:44 -05002668 * Flings the overscroller with the given velocity (anchor-based scrolling).
2669 *
2670 * Because anchor-based scrolling can't track the current scroll position, the overscroller is
2671 * always started at startY = 0, and we interpret the positions it computes as relative to the
2672 * start of the scroll.
2673 */
2674 private void flingScroller(int velocityY) {
2675 assert ANCHOR_SCROLLING;
2676 mIsScrollerBoundSet = false;
2677 maybeFlingScroller(velocityY, true /* always fling */);
2678 }
2679
2680 private void maybeFlingScroller(int velocityY, boolean alwaysFling) {
2681 assert ANCHOR_SCROLLING;
2682 // Attempt to determine the maximum amount to scroll before we reach the end.
2683 // If the first view is not materialized (for an upwards scroll) or the last view is either
2684 // not materialized or is pinned to the shade (for a downwards scroll), we don't know this
2685 // amount, so we do an unbounded fling and rely on {@link #maybeReflingScroller()} to update
2686 // the scroller once we approach the start/end of the list.
2687 int minY = Integer.MIN_VALUE;
2688 int maxY = Integer.MAX_VALUE;
2689 if (velocityY < 0) {
2690 minY = getMaxNegativeScrollAmount();
2691 if (minY > Integer.MIN_VALUE) {
2692 mIsScrollerBoundSet = true;
2693 }
2694 } else {
2695 maxY = getMaxPositiveScrollAmount();
2696 if (maxY < Integer.MAX_VALUE) {
2697 mIsScrollerBoundSet = true;
2698 }
2699 }
2700 if (mIsScrollerBoundSet || alwaysFling) {
2701 mLastScrollerY = 0;
2702 // x velocity is set to 1 to avoid overscroller bug
2703 mScroller.fling(0, 0, 1, velocityY, 0, 0, minY, maxY, 0,
2704 mExpandedInThisMotion && !isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2);
2705 }
2706 }
2707
2708 /**
2709 * Returns the maximum number of pixels we can scroll in the positive direction (downwards)
2710 * before reaching the bottom of the list (discounting overscroll).
2711 *
2712 * If the return value is negative then we have overscrolled; this is a transient state which
2713 * should immediately be handled by adjusting the anchor position and adding the extra space to
2714 * the bottom overscroll amount.
2715 *
2716 * If we don't know how many pixels we have left to scroll (because the last row has not been
2717 * materialized, or it's in the shelf so it doesn't have its "natural" position), we return
2718 * {@link Integer#MAX_VALUE}.
2719 */
2720 private int getMaxPositiveScrollAmount() {
2721 assert ANCHOR_SCROLLING;
2722 // TODO: once we're recycling we need to check the adapter position of the last child.
2723 ExpandableNotificationRow lastRow = getLastRowNotGone();
2724 if (mScrollAnchorView != null && lastRow != null && !lastRow.isInShelf()) {
2725 // distance from bottom of last child to bottom of notifications area is:
2726 // distance from bottom of last child
2727 return (int) (lastRow.getTranslationY() + lastRow.getActualHeight()
2728 // to top of anchor view
2729 - mScrollAnchorView.getTranslationY()
2730 // plus distance from anchor view to top of notifications area
2731 + mScrollAnchorViewY
2732 // minus height of notifications area.
2733 - (mMaxLayoutHeight - getIntrinsicPadding() - mFooterView.getActualHeight()));
2734 } else {
2735 return Integer.MAX_VALUE;
2736 }
2737 }
2738
2739 /**
2740 * Returns the maximum number of pixels (as a negative number) we can scroll in the negative
2741 * direction (upwards) before reaching the top of the list (discounting overscroll).
2742 *
2743 * If the return value is positive then we have overscrolled; this is a transient state which
2744 * should immediately be handled by adjusting the anchor position and adding the extra space to
2745 * the top overscroll amount.
2746 *
2747 * If we don't know how many pixels we have left to scroll (because the first row has not been
2748 * materialized), we return {@link Integer#MIN_VALUE}.
2749 */
2750 private int getMaxNegativeScrollAmount() {
2751 assert ANCHOR_SCROLLING;
2752 // TODO: once we're recycling we need to check the adapter position of the first child.
2753 ExpandableView firstChild = getFirstChildNotGone();
2754 if (mScrollAnchorView != null && firstChild != null) {
2755 // distance from top of first child to top of notifications area is:
2756 // distance from top of anchor view
2757 return (int) -(mScrollAnchorView.getTranslationY()
2758 // to top of first child
2759 - firstChild.getTranslationY()
2760 // minus distance from top of anchor view to top of notifications area.
2761 - mScrollAnchorViewY);
2762 } else {
2763 return Integer.MIN_VALUE;
2764 }
2765 }
2766
2767 /**
2768 * During a fling, if we were unable to set the bounds of the fling due to the top/bottom view
2769 * not being materialized or being pinned to the shelf, we need to check on every frame if we're
2770 * able to set the bounds. If we are, we fling the scroller again with the newly computed
2771 * bounds.
2772 */
2773 private void maybeReflingScroller() {
2774 if (!mIsScrollerBoundSet) {
2775 // Because mScroller is a flywheel scroller, we fling with the minimum possible
2776 // velocity to establish direction, so as not to perceptibly affect the velocity.
2777 maybeFlingScroller((int) Math.signum(mScroller.getCurrVelocity()),
2778 false /* alwaysFling */);
2779 }
2780 }
2781
2782 /**
Selim Cinek1408eb52014-06-02 14:45:38 +02002783 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2784 * overScroll view (i.e QS).
2785 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002786 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02002787 private boolean shouldOverScrollFling(int initialVelocity) {
2788 float topOverScroll = getCurrentOverScrollAmount(true);
2789 return mScrolledToTopOnFirstDown
2790 && !mExpandedInThisMotion
2791 && topOverScroll > mMinTopOverScrollToEscape
2792 && initialVelocity > 0;
2793 }
2794
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002795 /**
2796 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2797 * account.
2798 *
Jason Monke59dc402018-08-16 12:05:01 -04002799 * @param qsHeight the top padding imposed by the quick settings panel
2800 * @param animate whether to animate the change
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002801 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002802 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekdb6d6df2019-05-22 20:39:59 -07002803 public void updateTopPadding(float qsHeight, boolean animate) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002804 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002805 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002806 if (topPadding + minStackHeight > getHeight()) {
2807 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002808 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002809 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002810 }
Selim Cinekb0fada62019-06-17 19:03:59 -07002811 setTopPadding(topPadding, animate && !mKeyguardBypassController.getBypassEnabled());
Selim Cinekbc243a92016-09-27 16:35:13 -07002812 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002813 }
2814
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002815 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
shawnlin8e4e92c2018-04-12 18:47:24 +08002816 public void setMaxTopPadding(int maxTopPadding) {
2817 mMaxTopPadding = maxTopPadding;
2818 }
2819
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002820 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002821 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002822 if (isHeadsUpTransition()) {
2823 return getTopHeadsUpPinnedHeight();
2824 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002825 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002826 }
2827
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002828 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002829 public float getTopPaddingOverflow() {
2830 return mTopPaddingOverflow;
2831 }
2832
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002833 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002834 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002835 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002836 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002837 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002838 int shelfHeight = 0;
Gus Prevase2d6f042018-10-17 15:25:30 -04002839 if (getLastVisibleSection() != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002840 shelfHeight = mShelf.getIntrinsicHeight();
2841 }
2842 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002843 }
2844
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002845 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02002846 private int clampPadding(int desiredPadding) {
2847 return Math.max(desiredPadding, mIntrinsicPadding);
2848 }
2849
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002850 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfed1ab62014-06-17 14:10:33 -07002851 private float getRubberBandFactor(boolean onTop) {
2852 if (!onTop) {
2853 return RUBBER_BAND_FACTOR_NORMAL;
2854 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002855 if (mExpandedInThisMotion) {
2856 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002857 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002858 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2859 } else if (mScrolledToTopOnFirstDown) {
2860 return 1.0f;
2861 }
2862 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002863 }
2864
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002865 /**
2866 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2867 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2868 * overscroll view (e.g. expand QS).
2869 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002870 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002871 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002872 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002873 || !mScrolledToTopOnFirstDown;
2874 }
2875
Selim Cinek67b22602014-03-10 15:40:16 +01002876
Selim Cinek67b22602014-03-10 15:40:16 +01002877
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002878 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef5127e2015-12-21 16:55:58 -08002879 public void setChildTransferInProgress(boolean childTransferInProgress) {
Ned Burnsd4a69f72019-06-19 19:49:19 -04002880 Assert.isMainThread();
Selim Cinekef5127e2015-12-21 16:55:58 -08002881 mChildTransferInProgress = childTransferInProgress;
2882 }
2883
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002884 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Christoph Studer068f5922014-04-08 17:43:07 -04002885 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002886 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002887 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002888 // we only call our internal methods if this is actually a removal and not just a
2889 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002890 if (!mChildTransferInProgress) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002891 onViewRemovedInternal((ExpandableView) child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002892 }
2893 }
2894
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002895 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002896 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -05002897 public void cleanUpViewStateForEntry(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04002898 View child = entry.getRow();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04002899 if (child == mSwipeHelper.getTranslatingParentView()) {
2900 mSwipeHelper.clearTranslatingParentView();
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002901 }
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002902 }
2903
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002904 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002905 private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002906 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002907 // This is only a position change, don't do anything special
2908 return;
2909 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002910 child.setOnHeightChangedListener(null);
2911 updateScrollStateForRemovedChild(child);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002912 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002913 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002914 if (!mSwipedOutViews.contains(child)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002915 || Math.abs(child.getTranslation()) != child.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002916 container.addTransientView(child, 0);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002917 child.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002918 }
2919 } else {
2920 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002921 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002922 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002923
Selim Cineke9bad242016-06-15 11:46:37 -07002924 focusNextViewIfFocused(child);
2925 }
2926
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002927 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke9bad242016-06-15 11:46:37 -07002928 private void focusNextViewIfFocused(View view) {
2929 if (view instanceof ExpandableNotificationRow) {
2930 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2931 if (row.shouldRefocusOnDismiss()) {
2932 View nextView = row.getChildAfterViewWhenDismissed();
2933 if (nextView == null) {
2934 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2935 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2936 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002937 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002938 }
2939 if (nextView != null) {
2940 nextView.requestAccessibilityFocus();
2941 }
2942 }
2943 }
2944
Selim Cinekc27437b2014-05-14 10:23:33 +02002945 }
2946
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002947 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01002948 private boolean isChildInGroup(View child) {
2949 return child instanceof ExpandableNotificationRow
2950 && mGroupManager.isChildInGroupWithSummary(
Jason Monke59dc402018-08-16 12:05:01 -04002951 ((ExpandableNotificationRow) child).getStatusBarNotification());
Selim Cinekb5605e52015-02-20 18:21:41 +01002952 }
2953
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002954 /**
2955 * Generate a remove animation for a child view.
2956 *
2957 * @param child The view to generate the remove animation for.
2958 * @return Whether an animation was generated.
2959 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002960 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002961 private boolean generateRemoveAnimation(ExpandableView child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002962 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002963 mAddedHeadsUpChildren.remove(child);
2964 return false;
2965 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002966 if (isClickedHeadsUp(child)) {
Selim Cinek9dd0d042018-05-14 18:12:42 -07002967 // An animation is already running, add it transiently
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002968 mClearTransientViewsWhenFinished.add(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002969 return true;
2970 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002971 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002972 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002973 // Generate Animations
2974 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002975 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002976 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002977 } else {
2978 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002979 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002980 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002981 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002982 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002983 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002984 }
2985
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002986 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek0fccc722015-07-29 17:04:36 -07002987 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002988 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002989 }
2990
Selim Cineke0890e52015-06-17 11:17:08 -07002991 /**
2992 * Remove a removed child view from the heads up animations if it was just added there
2993 *
2994 * @return whether any child was removed from the list to animate
2995 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002996 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke0890e52015-06-17 11:17:08 -07002997 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2998 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002999 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
3000 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07003001 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003002 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07003003 mTmpList.add(eventPair);
3004 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003005 }
3006 }
Selim Cineke0890e52015-06-17 11:17:08 -07003007 if (hasAddEvent) {
3008 // This child was just added lets remove all events.
3009 mHeadsUpChangeAnimations.removeAll(mTmpList);
Jason Monke59dc402018-08-16 12:05:01 -04003010 ((ExpandableNotificationRow) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07003011 }
3012 mTmpList.clear();
3013 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003014 }
3015
Selim Cinek572bbd42014-04-25 16:43:27 +02003016 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01003017 * @param child the child to query
3018 * @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 -04003019 * not expanded
Selim Cinekb5605e52015-02-20 18:21:41 +01003020 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003021 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003022 private boolean isChildInInvisibleGroup(View child) {
3023 if (child instanceof ExpandableNotificationRow) {
3024 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Ned Burnsf81c4c42019-01-07 14:10:43 -05003025 NotificationEntry groupSummary =
Selim Cinekb5605e52015-02-20 18:21:41 +01003026 mGroupManager.getGroupSummary(row.getStatusBarNotification());
Evan Laird94492852018-10-25 13:43:01 -04003027 if (groupSummary != null && groupSummary.getRow() != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07003028 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01003029 }
3030 }
3031 return false;
3032 }
3033
3034 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02003035 * Updates the scroll position when a child was removed
3036 *
3037 * @param removedChild the removed child
3038 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003039 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek61633a82016-01-25 15:54:10 -08003040 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003041 if (ANCHOR_SCROLLING) {
3042 if (removedChild == mScrollAnchorView) {
3043 ExpandableView firstChild = getFirstChildNotGone();
3044 if (firstChild != null) {
3045 mScrollAnchorView = firstChild;
3046 } else {
3047 mScrollAnchorView = mShelf;
3048 }
3049 // Adjust anchor view Y by the distance between the old and new anchors
3050 // so that there's no visible change.
3051 mScrollAnchorViewY +=
3052 mScrollAnchorView.getTranslationY() - removedChild.getTranslationY();
3053 }
3054 updateScrollAnchor();
3055 // TODO: once we're recycling this will need to check the adapter position of the child
3056 if (mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY > 0) {
3057 mScrollAnchorViewY = 0;
3058 }
3059 updateOnScrollChange();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003060 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003061 int startingPosition = getPositionInLinearLayout(removedChild);
3062 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
3063 int padding;
3064 if (increasedPaddingAmount >= 0) {
3065 padding = (int) NotificationUtils.interpolate(
3066 mPaddingBetweenElements,
3067 mIncreasedPaddingBetweenElements,
3068 increasedPaddingAmount);
3069 } else {
3070 padding = (int) NotificationUtils.interpolate(
3071 0,
3072 mPaddingBetweenElements,
3073 1.0f + increasedPaddingAmount);
3074 }
3075 int childHeight = getIntrinsicHeight(removedChild) + padding;
3076 int endPosition = startingPosition + childHeight;
3077 if (endPosition <= mOwnScrollY) {
3078 // This child is fully scrolled of the top, so we have to deduct its height from the
3079 // scrollPosition
3080 setOwnScrollY(mOwnScrollY - childHeight);
3081 } else if (startingPosition < mOwnScrollY) {
3082 // This child is currently being scrolled into, set the scroll position to the
3083 // start of this child
3084 setOwnScrollY(startingPosition);
3085 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003086 }
3087 }
3088
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003089 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd7c4e002014-07-04 18:36:42 +02003090 private int getIntrinsicHeight(View view) {
3091 if (view instanceof ExpandableView) {
3092 ExpandableView expandableView = (ExpandableView) view;
3093 return expandableView.getIntrinsicHeight();
3094 }
3095 return view.getHeight();
3096 }
3097
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003098 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1f624952017-06-08 19:11:50 -07003099 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07003100 ExpandableNotificationRow childInGroup = null;
3101 ExpandableNotificationRow requestedRow = null;
3102 if (isChildInGroup(requestedView)) {
3103 // We're asking for a child in a group. Calculate the position of the parent first,
3104 // then within the parent.
3105 childInGroup = (ExpandableNotificationRow) requestedView;
3106 requestedView = requestedRow = childInGroup.getNotificationParent();
3107 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003108 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08003109 float previousPaddingRequest = mPaddingBetweenElements;
3110 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02003111 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08003112 ExpandableView child = (ExpandableView) getChildAt(i);
3113 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07003114 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08003115 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003116 float padding;
3117 if (increasedPaddingAmount >= 0.0f) {
3118 padding = (int) NotificationUtils.interpolate(
3119 previousPaddingRequest,
3120 mIncreasedPaddingBetweenElements,
3121 increasedPaddingAmount);
3122 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08003123 mPaddingBetweenElements,
3124 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08003125 increasedPaddingAmount);
3126 } else {
3127 int ownPadding = (int) NotificationUtils.interpolate(
3128 0,
3129 mPaddingBetweenElements,
3130 1.0f + increasedPaddingAmount);
3131 if (previousPaddingAmount > 0.0f) {
3132 padding = (int) NotificationUtils.interpolate(
3133 ownPadding,
3134 mIncreasedPaddingBetweenElements,
3135 previousPaddingAmount);
3136 } else {
3137 padding = ownPadding;
3138 }
3139 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08003140 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08003141 if (position != 0) {
3142 position += padding;
3143 }
3144 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08003145 }
Adrian Roos4a579672016-05-24 16:54:37 -07003146 if (child == requestedView) {
3147 if (requestedRow != null) {
3148 position += requestedRow.getPositionOfChild(childInGroup);
3149 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003150 return position;
3151 }
Selim Cinek61633a82016-01-25 15:54:10 -08003152 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02003153 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003154 }
3155 }
3156 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02003157 }
3158
3159 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003160 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adam Powell6690d012015-06-17 16:41:56 -07003161 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02003162 super.onViewAdded(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003163 onViewAddedInternal((ExpandableView) child);
Selim Cinekb5605e52015-02-20 18:21:41 +01003164 }
3165
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003166 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08003167 private void updateFirstAndLastBackgroundViews() {
Gus Prevase2d6f042018-10-17 15:25:30 -04003168 NotificationSection firstSection = getFirstVisibleSection();
3169 NotificationSection lastSection = getLastVisibleSection();
Gus Prevasda13cfa2018-11-20 14:58:48 -05003170 ActivatableNotificationView previousFirstChild =
3171 firstSection == null ? null : firstSection.getFirstVisibleChild();
3172 ActivatableNotificationView previousLastChild =
3173 lastSection == null ? null : lastSection.getLastVisibleChild();
Gus Prevase2d6f042018-10-17 15:25:30 -04003174
Selim Cinek614576e2016-01-20 10:54:09 -08003175 ActivatableNotificationView firstChild = getFirstChildWithBackground();
3176 ActivatableNotificationView lastChild = getLastChildWithBackground();
Ned Burns9eb06332019-04-23 16:02:12 -04003177 boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsInSections(
Gus Prevase2d6f042018-10-17 15:25:30 -04003178 mSections[0], mSections[1], firstChild, lastChild);
3179
Selim Cinek614576e2016-01-20 10:54:09 -08003180 if (mAnimationsEnabled && mIsExpanded) {
Gus Prevasda13cfa2018-11-20 14:58:48 -05003181 mAnimateNextBackgroundTop = firstChild != previousFirstChild;
Selim Cinek6f0a62a2019-04-09 18:40:12 -07003182 mAnimateNextBackgroundBottom = lastChild != previousLastChild || mAnimateBottomOnLayout;
Gus Prevase2d6f042018-10-17 15:25:30 -04003183 mAnimateNextSectionBoundsChange = sectionViewsChanged;
Selim Cinek614576e2016-01-20 10:54:09 -08003184 } else {
3185 mAnimateNextBackgroundTop = false;
3186 mAnimateNextBackgroundBottom = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04003187 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08003188 }
Selim Cinekdb167372016-11-17 15:41:17 -08003189 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Gus Prevase2d6f042018-10-17 15:25:30 -04003190 mRoundnessManager.updateRoundedChildren(mSections);
Selim Cinek6f0a62a2019-04-09 18:40:12 -07003191 mAnimateBottomOnLayout = false;
Selim Cinek515b2032017-11-15 10:20:19 -08003192 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003193 }
3194
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003195 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003196 private void onViewAddedInternal(ExpandableView child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003197 updateHideSensitiveForChild(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003198 child.setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003199 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003200 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003201 updateChronometerForChild(child);
Gus Prevasa18dc572019-01-14 16:11:22 -05003202 if (child instanceof ExpandableNotificationRow) {
3203 ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
3204 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05003205 if (ANCHOR_SCROLLING) {
3206 // TODO: once we're recycling this will need to check the adapter position of the child
3207 if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) {
3208 // New child was added at the top while we're scrolled to the top;
3209 // make it the new anchor view so that we stay at the top.
3210 mScrollAnchorView = child;
3211 }
3212 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003213 }
3214
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003215 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003216 private void updateHideSensitiveForChild(ExpandableView child) {
3217 child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003218 }
3219
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003220 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003221 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003222 public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
Selim Cinekd1395642016-04-28 12:22:42 -07003223 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003224 }
3225
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003226 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003227 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003228 public void notifyGroupChildAdded(ExpandableView row) {
Selim Cinekb5605e52015-02-20 18:21:41 +01003229 onViewAddedInternal(row);
3230 }
3231
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003232 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003233 public void setAnimationsEnabled(boolean animationsEnabled) {
3234 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003235 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003236 if (!animationsEnabled) {
3237 mSwipedOutViews.clear();
3238 mChildrenToRemoveAnimated.clear();
3239 clearTemporaryViewsInGroup(this);
3240 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003241 }
3242
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003243 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003244 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003245 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003246 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003247 int childCount = getChildCount();
3248 for (int i = 0; i < childCount; i++) {
3249 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003250 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003251 updateAnimationState(running, child);
3252 }
3253 }
3254
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003255 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek51ae05d2014-09-09 15:51:38 +02003256 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003257 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003258 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003259 }
3260
Selim Cinek2627d722018-01-19 12:16:49 -08003261 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003262 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek2627d722018-01-19 12:16:49 -08003263 public void setExpandingNotification(ExpandableNotificationRow row) {
3264 mAmbientState.setExpandingNotification(row);
3265 requestChildrenUpdate();
3266 }
3267
3268 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003269 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek8875de12018-03-22 10:14:32 -07003270 public void bindRow(ExpandableNotificationRow row) {
Selim Cinekf0c79e12018-05-14 17:17:31 -07003271 row.setHeadsUpAnimatingAwayListener(animatingAway -> {
3272 mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
3273 mHeadsUpAppearanceController.updateHeader(row.getEntry());
3274 });
Selim Cinek8875de12018-03-22 10:14:32 -07003275 }
3276
3277 @Override
Selim Cinekfdf80332019-03-07 17:29:55 -08003278 public boolean containsView(View v) {
3279 return v.getParent() == this;
3280 }
3281
3282 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003283 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek2627d722018-01-19 12:16:49 -08003284 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3285 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3286 requestChildrenUpdate();
3287 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003288
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003289 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003290 private void updateAnimationState(boolean running, View child) {
3291 if (child instanceof ExpandableNotificationRow) {
3292 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3293 row.setIconAnimationRunning(running);
3294 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003295 }
3296
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003297 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003298 public boolean isAddOrRemoveAnimationPending() {
3299 return mNeedsAnimation
3300 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3301 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003302
3303 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003304 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003305 public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
Selim Cinek820ba2d2019-06-18 18:59:09 -07003306 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress && !isFullyHidden()) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003307 // Generate Animations
3308 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003309 if (fromMoreCard) {
3310 mFromMoreCardAdditions.add(child);
3311 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003312 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003313 }
Selim Cinek820ba2d2019-06-18 18:59:09 -07003314 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress
3315 && !isFullyHidden()) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003316 mAddedHeadsUpChildren.add(child);
3317 mChildrenToAddAnimated.remove(child);
3318 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003319 }
3320
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003321 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003322 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003323 public void changeViewPosition(ExpandableView child, int newIndex) {
Ned Burnsd4a69f72019-06-19 19:49:19 -04003324 Assert.isMainThread();
3325 if (mChangePositionInProgress) {
3326 throw new IllegalStateException("Reentrant call to changeViewPosition");
3327 }
3328
Dan Sandlereceda3d2014-07-21 15:35:01 -04003329 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003330
3331 if (currentIndex == -1) {
3332 boolean isTransient = false;
3333 if (child instanceof ExpandableNotificationRow
Jason Monke59dc402018-08-16 12:05:01 -04003334 && ((ExpandableNotificationRow) child).getTransientContainer() != null) {
Rohan Shah8ee53652018-04-05 11:13:50 -07003335 isTransient = true;
3336 }
3337 Log.e(TAG, "Attempting to re-position "
3338 + (isTransient ? "transient" : "")
3339 + " view {"
3340 + child
3341 + "}");
3342 return;
3343 }
3344
Dan Sandlereceda3d2014-07-21 15:35:01 -04003345 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003346 mChangePositionInProgress = true;
Jason Monke59dc402018-08-16 12:05:01 -04003347 ((ExpandableView) child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003348 removeView(child);
3349 addView(child, newIndex);
Jason Monke59dc402018-08-16 12:05:01 -04003350 ((ExpandableView) child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003351 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003352 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003353 mChildrenChangingPositions.add(child);
3354 mNeedsAnimation = true;
3355 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003356 }
3357 }
3358
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003359 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf4c19962014-05-01 21:55:31 +02003360 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003361 if (mNeedsAnimation) {
Rohan Shahb9d500a2018-06-25 16:27:16 -07003362 generateAllAnimationEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003363 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003364 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003365 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003366 setAnimationRunning(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003367 mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003368 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003369 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003370 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003371 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003372 } else {
3373 applyCurrentState();
3374 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003375 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003376 }
3377
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003378 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shahb9d500a2018-06-25 16:27:16 -07003379 private void generateAllAnimationEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003380 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003381 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003382 generateChildAdditionEvents();
3383 generatePositionChangeEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003384 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003385 generateActivateEvent();
3386 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003387 generateHideSensitiveEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003388 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003389 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003390 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003391 generateAnimateEverythingEvent();
Selim Cinek572bbd42014-04-25 16:43:27 +02003392 }
3393
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003394 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003395 private void generateHeadsUpAnimationEvents() {
3396 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003397 ExpandableNotificationRow row = eventPair.first;
3398 boolean isHeadsUp = eventPair.second;
3399 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3400 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003401 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003402 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003403 type = row.wasJustClicked()
3404 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3405 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003406 if (row.isChildInGroup()) {
3407 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003408 row.setHeadsUpAnimatingAway(false);
Selim Cinekf93bf3e2018-05-08 14:43:21 -07003409 continue;
Selim Cinek76e813c2016-07-14 11:16:58 -07003410 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003411 } else {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003412 ExpandableViewState viewState = row.getViewState();
Selim Cinekeaee9c02015-06-25 11:04:20 -04003413 if (viewState == null) {
3414 // A view state was never generated for this view, so we don't need to animate
3415 // this. This may happen with notification children.
3416 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003417 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003418 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3419 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3420 // Our custom add animation
3421 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3422 } else {
3423 // Normal add animation
3424 type = AnimationEvent.ANIMATION_TYPE_ADD;
3425 }
3426 onBottom = !pinnedAndClosed;
3427 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003428 }
3429 AnimationEvent event = new AnimationEvent(row, type);
3430 event.headsUpFromBottom = onBottom;
3431 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003432 }
3433 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003434 mAddedHeadsUpChildren.clear();
3435 }
3436
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003437 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbbcebde2016-11-09 18:28:20 -08003438 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003439 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3440 return false;
3441 }
3442 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003443 }
3444
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003445 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003446 private void generateGroupExpansionEvent() {
3447 // Generate a group expansion/collapsing event if there is such a group at all
3448 if (mExpandedGroupView != null) {
3449 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3450 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3451 mExpandedGroupView = null;
3452 }
3453 }
3454
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003455 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineka5e211b2014-08-11 17:35:48 +02003456 private void generateViewResizeEvent() {
3457 if (mNeedViewResizeAnimation) {
felkachangd7835b02018-07-17 18:18:13 +08003458 boolean hasDisappearAnimation = false;
3459 for (AnimationEvent animationEvent : mAnimationEvents) {
3460 final int type = animationEvent.animationType;
3461 if (type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
Jason Monke59dc402018-08-16 12:05:01 -04003462 || type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
felkachangd7835b02018-07-17 18:18:13 +08003463 hasDisappearAnimation = true;
3464 break;
3465 }
3466 }
3467
3468 if (!hasDisappearAnimation) {
3469 mAnimationEvents.add(
3470 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3471 }
Selim Cineka5e211b2014-08-11 17:35:48 +02003472 }
3473 mNeedViewResizeAnimation = false;
3474 }
3475
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003476 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003477 private void generateChildRemovalEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003478 for (ExpandableView child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003479 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003480
3481 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003482 float removedTranslation = child.getTranslationY();
3483 boolean ignoreChildren = true;
3484 if (child instanceof ExpandableNotificationRow) {
3485 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3486 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3487 removedTranslation = row.getTranslationWhenRemoved();
3488 ignoreChildren = false;
3489 }
Selim Cinek51052042017-07-04 12:07:55 +02003490 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003491 }
Selim Cinek51052042017-07-04 12:07:55 +02003492 if (!childWasSwipedOut) {
3493 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003494 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Rohan Shaha7594962018-05-22 10:59:30 -07003495
3496 if (childWasSwipedOut && child instanceof ExpandableView) {
3497 // Clean up any potential transient views if the child has already been swiped
3498 // out, as we won't be animating it further (due to its height already being
3499 // clipped to 0.
3500 ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
3501 if (transientContainer != null) {
3502 transientContainer.removeTransientView(child);
3503 }
3504 }
Selim Cinek51052042017-07-04 12:07:55 +02003505 }
3506 int animationType = childWasSwipedOut
3507 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3508 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3509 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003510 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3511 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003512 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003513 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003514 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003515 mChildrenToRemoveAnimated.clear();
3516 }
3517
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003518 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003519 private void generatePositionChangeEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003520 for (ExpandableView child : mChildrenChangingPositions) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003521 mAnimationEvents.add(new AnimationEvent(child,
3522 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3523 }
3524 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003525 if (mGenerateChildOrderChangedEvent) {
3526 mAnimationEvents.add(new AnimationEvent(null,
3527 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3528 mGenerateChildOrderChangedEvent = false;
3529 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003530 }
3531
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003532 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003533 private void generateChildAdditionEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003534 for (ExpandableView child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003535 if (mFromMoreCardAdditions.contains(child)) {
3536 mAnimationEvents.add(new AnimationEvent(child,
3537 AnimationEvent.ANIMATION_TYPE_ADD,
3538 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3539 } else {
3540 mAnimationEvents.add(new AnimationEvent(child,
3541 AnimationEvent.ANIMATION_TYPE_ADD));
3542 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003543 }
3544 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003545 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003546 }
3547
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003548 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003549 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003550 if (mTopPaddingNeedsAnimation) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003551 AnimationEvent event;
Selim Cinekc1d9ab22019-05-21 18:08:30 -07003552 if (mAmbientState.isDozing()) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003553 event = new AnimationEvent(null /* view */,
3554 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
3555 KeyguardSliceView.DEFAULT_ANIM_DURATION);
3556 } else {
3557 event = new AnimationEvent(null /* view */,
3558 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
3559 }
3560 mAnimationEvents.add(event);
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003561 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003562 mTopPaddingNeedsAnimation = false;
3563 }
3564
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003565 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003566 private void generateActivateEvent() {
3567 if (mActivateNeedsAnimation) {
3568 mAnimationEvents.add(
3569 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3570 }
3571 mActivateNeedsAnimation = false;
3572 }
3573
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003574 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd9acca52014-09-01 22:33:25 +02003575 private void generateAnimateEverythingEvent() {
3576 if (mEverythingNeedsAnimation) {
3577 mAnimationEvents.add(
3578 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3579 }
3580 mEverythingNeedsAnimation = false;
3581 }
3582
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003583 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003584 private void generateDimmedEvent() {
3585 if (mDimmedNeedsAnimation) {
3586 mAnimationEvents.add(
3587 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3588 }
3589 mDimmedNeedsAnimation = false;
3590 }
3591
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003592 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggiae441282014-08-01 02:45:18 +02003593 private void generateHideSensitiveEvent() {
3594 if (mHideSensitiveNeedsAnimation) {
3595 mAnimationEvents.add(
3596 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3597 }
3598 mHideSensitiveNeedsAnimation = false;
3599 }
3600
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003601 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003602 private void generateGoToFullShadeEvent() {
3603 if (mGoToFullShadeNeedsAnimation) {
3604 mAnimationEvents.add(
3605 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3606 }
3607 mGoToFullShadeNeedsAnimation = false;
3608 }
3609
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003610 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
3611 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003612 return new StackScrollAlgorithm(context, this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003613 }
3614
3615 /**
3616 * @return Whether a y coordinate is inside the content.
3617 */
3618 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
3619 public boolean isInContentBounds(float y) {
3620 return y < getHeight() - getEmptyBottomMargin();
3621 }
3622
3623 @ShadeViewRefactor(RefactorComponent.INPUT)
3624 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
3625 mLongPressListener = listener;
3626 }
3627
3628 @Override
3629 @ShadeViewRefactor(RefactorComponent.INPUT)
3630 public boolean onTouchEvent(MotionEvent ev) {
3631 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
3632 || ev.getActionMasked() == MotionEvent.ACTION_UP;
3633 handleEmptySpaceClick(ev);
3634 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003635 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003636 if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
3637 if (isCancelOrUp) {
3638 mExpandHelper.onlyObserveMovements(false);
3639 }
3640 boolean wasExpandingBefore = mExpandingNotification;
3641 expandWantsIt = mExpandHelper.onTouchEvent(ev);
3642 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
3643 && !mDisallowScrollingInThisMotion) {
3644 dispatchDownEventToScroller(ev);
3645 }
3646 }
3647 boolean scrollerWantsIt = false;
3648 if (mIsExpanded && !swipingInProgress && !mExpandingNotification
3649 && !mDisallowScrollingInThisMotion) {
3650 scrollerWantsIt = onScrollTouch(ev);
3651 }
3652 boolean horizontalSwipeWantsIt = false;
3653 if (!mIsBeingDragged
3654 && !mExpandingNotification
3655 && !mExpandedInThisMotion
3656 && !mOnlyScrollingInThisMotion
3657 && !mDisallowDismissInThisMotion) {
3658 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
3659 }
3660
3661 // Check if we need to clear any snooze leavebehinds
3662 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
3663 if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
3664 && guts.getGutsContent() instanceof NotificationSnooze) {
3665 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
3666 if ((ns.isExpanded() && isCancelOrUp)
3667 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
3668 // If the leavebehind is expanded we clear it on the next up event, otherwise we
3669 // clear it on the next non-horizontal swipe or expand event.
3670 checkSnoozeLeavebehind();
3671 }
3672 }
3673 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
3674 mCheckForLeavebehind = true;
3675 }
3676 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
3677 }
3678
3679 @ShadeViewRefactor(RefactorComponent.INPUT)
3680 private void dispatchDownEventToScroller(MotionEvent ev) {
3681 MotionEvent downEvent = MotionEvent.obtain(ev);
3682 downEvent.setAction(MotionEvent.ACTION_DOWN);
3683 onScrollTouch(downEvent);
3684 downEvent.recycle();
3685 }
3686
3687 @Override
3688 @ShadeViewRefactor(RefactorComponent.INPUT)
3689 public boolean onGenericMotionEvent(MotionEvent event) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003690 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003691 || mDisallowScrollingInThisMotion) {
3692 return false;
3693 }
3694 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
3695 switch (event.getAction()) {
3696 case MotionEvent.ACTION_SCROLL: {
3697 if (!mIsBeingDragged) {
3698 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3699 if (vscroll != 0) {
3700 final int delta = (int) (vscroll * getVerticalScrollFactor());
Gus Prevas0fa58d62019-01-11 13:58:40 -05003701 if (ANCHOR_SCROLLING) {
3702 mScrollAnchorViewY -= delta;
3703 updateScrollAnchor();
3704 clampScrollPosition();
3705 updateOnScrollChange();
3706 } else {
3707 final int range = getScrollRange();
3708 int oldScrollY = mOwnScrollY;
3709 int newScrollY = oldScrollY - delta;
3710 if (newScrollY < 0) {
3711 newScrollY = 0;
3712 } else if (newScrollY > range) {
3713 newScrollY = range;
3714 }
3715 if (newScrollY != oldScrollY) {
3716 setOwnScrollY(newScrollY);
3717 return true;
3718 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003719 }
3720 }
3721 }
3722 }
3723 }
3724 }
3725 return super.onGenericMotionEvent(event);
3726 }
3727
3728 @ShadeViewRefactor(RefactorComponent.INPUT)
3729 private boolean onScrollTouch(MotionEvent ev) {
3730 if (!isScrollingEnabled()) {
3731 return false;
3732 }
3733 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
3734 return false;
3735 }
3736 mForcedScroll = null;
3737 initVelocityTrackerIfNotExists();
3738 mVelocityTracker.addMovement(ev);
3739
3740 final int action = ev.getAction();
3741
3742 switch (action & MotionEvent.ACTION_MASK) {
3743 case MotionEvent.ACTION_DOWN: {
3744 if (getChildCount() == 0 || !isInContentBounds(ev)) {
3745 return false;
3746 }
3747 boolean isBeingDragged = !mScroller.isFinished();
3748 setIsBeingDragged(isBeingDragged);
3749 /*
3750 * If being flinged and user touches, stop the fling. isFinished
3751 * will be false if being flinged.
3752 */
3753 if (!mScroller.isFinished()) {
3754 mScroller.forceFinished(true);
3755 }
3756
3757 // Remember where the motion event started
3758 mLastMotionY = (int) ev.getY();
3759 mDownX = (int) ev.getX();
3760 mActivePointerId = ev.getPointerId(0);
3761 break;
3762 }
3763 case MotionEvent.ACTION_MOVE:
3764 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
3765 if (activePointerIndex == -1) {
3766 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
3767 break;
3768 }
3769
3770 final int y = (int) ev.getY(activePointerIndex);
3771 final int x = (int) ev.getX(activePointerIndex);
3772 int deltaY = mLastMotionY - y;
3773 final int xDiff = Math.abs(x - mDownX);
3774 final int yDiff = Math.abs(deltaY);
3775 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
3776 setIsBeingDragged(true);
3777 if (deltaY > 0) {
3778 deltaY -= mTouchSlop;
3779 } else {
3780 deltaY += mTouchSlop;
3781 }
3782 }
3783 if (mIsBeingDragged) {
3784 // Scroll to follow the motion event
3785 mLastMotionY = y;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003786 float scrollAmount;
Gus Prevas0fa58d62019-01-11 13:58:40 -05003787 int range;
3788 if (ANCHOR_SCROLLING) {
3789 range = 0; // unused in the methods it's being passed to
3790 } else {
3791 range = getScrollRange();
3792 if (mExpandedInThisMotion) {
3793 range = Math.min(range, mMaxScrollAfterExpand);
3794 }
3795 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003796 if (deltaY < 0) {
3797 scrollAmount = overScrollDown(deltaY);
3798 } else {
3799 scrollAmount = overScrollUp(deltaY, range);
3800 }
3801
3802 // Calling customOverScrollBy will call onCustomOverScrolled, which
3803 // sets the scrolling if applicable.
3804 if (scrollAmount != 0.0f) {
3805 // The scrolling motion could not be compensated with the
3806 // existing overScroll, we have to scroll the view
3807 customOverScrollBy((int) scrollAmount, mOwnScrollY,
3808 range, getHeight() / 2);
3809 // If we're scrolling, leavebehinds should be dismissed
3810 checkSnoozeLeavebehind();
3811 }
3812 }
3813 break;
3814 case MotionEvent.ACTION_UP:
3815 if (mIsBeingDragged) {
3816 final VelocityTracker velocityTracker = mVelocityTracker;
3817 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
3818 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
3819
3820 if (shouldOverScrollFling(initialVelocity)) {
3821 onOverScrollFling(true, initialVelocity);
3822 } else {
3823 if (getChildCount() > 0) {
3824 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
3825 float currentOverScrollTop = getCurrentOverScrollAmount(true);
3826 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
3827 fling(-initialVelocity);
3828 } else {
3829 onOverScrollFling(false, initialVelocity);
3830 }
3831 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003832 if (ANCHOR_SCROLLING) {
3833 // TODO
3834 } else {
3835 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3836 getScrollRange())) {
3837 animateScroll();
3838 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003839 }
3840 }
3841 }
3842 }
3843 mActivePointerId = INVALID_POINTER;
3844 endDrag();
3845 }
3846
3847 break;
3848 case MotionEvent.ACTION_CANCEL:
3849 if (mIsBeingDragged && getChildCount() > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003850 if (ANCHOR_SCROLLING) {
3851 // TODO
3852 } else {
3853 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3854 getScrollRange())) {
3855 animateScroll();
3856 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003857 }
3858 mActivePointerId = INVALID_POINTER;
3859 endDrag();
3860 }
3861 break;
3862 case MotionEvent.ACTION_POINTER_DOWN: {
3863 final int index = ev.getActionIndex();
3864 mLastMotionY = (int) ev.getY(index);
3865 mDownX = (int) ev.getX(index);
3866 mActivePointerId = ev.getPointerId(index);
3867 break;
3868 }
3869 case MotionEvent.ACTION_POINTER_UP:
3870 onSecondaryPointerUp(ev);
3871 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
3872 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
3873 break;
3874 }
3875 return true;
3876 }
3877
3878 @ShadeViewRefactor(RefactorComponent.INPUT)
3879 protected boolean isInsideQsContainer(MotionEvent ev) {
3880 return ev.getY() < mQsContainer.getBottom();
3881 }
3882
3883 @ShadeViewRefactor(RefactorComponent.INPUT)
3884 private void onOverScrollFling(boolean open, int initialVelocity) {
3885 if (mOverscrollTopChangedListener != null) {
3886 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
3887 }
3888 mDontReportNextOverScroll = true;
3889 setOverScrollAmount(0.0f, true, false);
3890 }
3891
3892
3893 @ShadeViewRefactor(RefactorComponent.INPUT)
3894 private void onSecondaryPointerUp(MotionEvent ev) {
3895 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
3896 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
3897 final int pointerId = ev.getPointerId(pointerIndex);
3898 if (pointerId == mActivePointerId) {
3899 // This was our active pointer going up. Choose a new
3900 // active pointer and adjust accordingly.
3901 // TODO: Make this decision more intelligent.
3902 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
3903 mLastMotionY = (int) ev.getY(newPointerIndex);
3904 mActivePointerId = ev.getPointerId(newPointerIndex);
3905 if (mVelocityTracker != null) {
3906 mVelocityTracker.clear();
3907 }
3908 }
3909 }
3910
3911 @ShadeViewRefactor(RefactorComponent.INPUT)
3912 private void endDrag() {
3913 setIsBeingDragged(false);
3914
3915 recycleVelocityTracker();
3916
3917 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
3918 setOverScrollAmount(0, true /* onTop */, true /* animate */);
3919 }
3920 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
3921 setOverScrollAmount(0, false /* onTop */, true /* animate */);
3922 }
3923 }
3924
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003925 @Override
3926 @ShadeViewRefactor(RefactorComponent.INPUT)
3927 public boolean onInterceptTouchEvent(MotionEvent ev) {
3928 initDownStates(ev);
3929 handleEmptySpaceClick(ev);
3930 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003931 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003932 if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
3933 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
3934 }
3935 boolean scrollWantsIt = false;
3936 if (!swipingInProgress && !mExpandingNotification) {
3937 scrollWantsIt = onInterceptTouchEventScroll(ev);
3938 }
3939 boolean swipeWantsIt = false;
3940 if (!mIsBeingDragged
3941 && !mExpandingNotification
3942 && !mExpandedInThisMotion
3943 && !mOnlyScrollingInThisMotion
3944 && !mDisallowDismissInThisMotion) {
3945 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
3946 }
3947 // Check if we need to clear any snooze leavebehinds
3948 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
3949 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
3950 if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
3951 !expandWantsIt && !scrollWantsIt) {
3952 mCheckForLeavebehind = false;
3953 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
3954 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3955 false /* resetMenu */);
3956 }
3957 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
3958 mCheckForLeavebehind = true;
3959 }
3960 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
3961 }
3962
3963 @ShadeViewRefactor(RefactorComponent.INPUT)
3964 private void handleEmptySpaceClick(MotionEvent ev) {
3965 switch (ev.getActionMasked()) {
3966 case MotionEvent.ACTION_MOVE:
3967 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
3968 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
3969 mTouchIsClick = false;
3970 }
3971 break;
3972 case MotionEvent.ACTION_UP:
3973 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
3974 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
3975 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
3976 }
3977 break;
3978 }
3979 }
3980
3981 @ShadeViewRefactor(RefactorComponent.INPUT)
3982 private void initDownStates(MotionEvent ev) {
3983 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
3984 mExpandedInThisMotion = false;
3985 mOnlyScrollingInThisMotion = !mScroller.isFinished();
3986 mDisallowScrollingInThisMotion = false;
3987 mDisallowDismissInThisMotion = false;
3988 mTouchIsClick = true;
3989 mInitialTouchX = ev.getX();
3990 mInitialTouchY = ev.getY();
3991 }
3992 }
3993
3994 @Override
3995 @ShadeViewRefactor(RefactorComponent.INPUT)
3996 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3997 super.requestDisallowInterceptTouchEvent(disallowIntercept);
3998 if (disallowIntercept) {
3999 cancelLongPress();
4000 }
4001 }
4002
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004003 @ShadeViewRefactor(RefactorComponent.INPUT)
Selim Cinek67b22602014-03-10 15:40:16 +01004004 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02004005 if (!isScrollingEnabled()) {
4006 return false;
4007 }
Selim Cinek67b22602014-03-10 15:40:16 +01004008 /*
4009 * This method JUST determines whether we want to intercept the motion.
4010 * If we return true, onMotionEvent will be called and we do the actual
4011 * scrolling there.
4012 */
4013
4014 /*
Jason Monke59dc402018-08-16 12:05:01 -04004015 * Shortcut the most recurring case: the user is in the dragging
4016 * state and is moving their finger. We want to intercept this
4017 * motion.
4018 */
Selim Cinek67b22602014-03-10 15:40:16 +01004019 final int action = ev.getAction();
4020 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
4021 return true;
4022 }
4023
Selim Cinek67b22602014-03-10 15:40:16 +01004024 switch (action & MotionEvent.ACTION_MASK) {
4025 case MotionEvent.ACTION_MOVE: {
4026 /*
4027 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04004028 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01004029 */
4030
4031 /*
Jason Monke59dc402018-08-16 12:05:01 -04004032 * Locally do absolute value. mLastMotionY is set to the y value
4033 * of the down event.
4034 */
Selim Cinek67b22602014-03-10 15:40:16 +01004035 final int activePointerId = mActivePointerId;
4036 if (activePointerId == INVALID_POINTER) {
4037 // If we don't have a valid id, the touch down wasn't on content.
4038 break;
4039 }
4040
4041 final int pointerIndex = ev.findPointerIndex(activePointerId);
4042 if (pointerIndex == -1) {
4043 Log.e(TAG, "Invalid pointerId=" + activePointerId
4044 + " in onInterceptTouchEvent");
4045 break;
4046 }
4047
4048 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02004049 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01004050 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02004051 final int xDiff = Math.abs(x - mDownX);
4052 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01004053 setIsBeingDragged(true);
4054 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004055 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01004056 initVelocityTrackerIfNotExists();
4057 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01004058 }
4059 break;
4060 }
4061
4062 case MotionEvent.ACTION_DOWN: {
4063 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05304064 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07004065 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01004066 setIsBeingDragged(false);
4067 recycleVelocityTracker();
4068 break;
4069 }
4070
4071 /*
4072 * Remember location of down touch.
4073 * ACTION_DOWN always refers to pointer index 0.
4074 */
4075 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004076 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01004077 mActivePointerId = ev.getPointerId(0);
4078
4079 initOrResetVelocityTracker();
4080 mVelocityTracker.addMovement(ev);
4081 /*
Jason Monke59dc402018-08-16 12:05:01 -04004082 * If being flinged and user touches the screen, initiate drag;
4083 * otherwise don't. mScroller.isFinished should be false when
4084 * being flinged.
4085 */
Selim Cinek67b22602014-03-10 15:40:16 +01004086 boolean isBeingDragged = !mScroller.isFinished();
4087 setIsBeingDragged(isBeingDragged);
4088 break;
4089 }
4090
4091 case MotionEvent.ACTION_CANCEL:
4092 case MotionEvent.ACTION_UP:
4093 /* Release the drag */
4094 setIsBeingDragged(false);
4095 mActivePointerId = INVALID_POINTER;
4096 recycleVelocityTracker();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004097 if (ANCHOR_SCROLLING) {
4098 // TODO
4099 } else {
4100 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
4101 animateScroll();
4102 }
Selim Cinek67b22602014-03-10 15:40:16 +01004103 }
4104 break;
4105 case MotionEvent.ACTION_POINTER_UP:
4106 onSecondaryPointerUp(ev);
4107 break;
4108 }
4109
4110 /*
Jason Monke59dc402018-08-16 12:05:01 -04004111 * The only time we want to intercept motion events is if we are in the
4112 * drag mode.
4113 */
Selim Cinek67b22602014-03-10 15:40:16 +01004114 return mIsBeingDragged;
4115 }
4116
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004117 /**
4118 * @return Whether the specified motion event is actually happening over the content.
4119 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004120 @ShadeViewRefactor(RefactorComponent.INPUT)
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004121 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01004122 return isInContentBounds(event.getY());
4123 }
4124
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004125
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004126 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004127 @ShadeViewRefactor(RefactorComponent.INPUT)
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004128 void setIsBeingDragged(boolean isDragged) {
Selim Cinek67b22602014-03-10 15:40:16 +01004129 mIsBeingDragged = isDragged;
4130 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004131 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004132 cancelLongPress();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004133 resetExposedMenuView(true /* animate */, true /* force */);
Selim Cinek67b22602014-03-10 15:40:16 +01004134 }
4135 }
4136
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004137 @ShadeViewRefactor(RefactorComponent.INPUT)
4138 public void requestDisallowLongPress() {
4139 cancelLongPress();
4140 }
4141
4142 @ShadeViewRefactor(RefactorComponent.INPUT)
4143 public void requestDisallowDismiss() {
4144 mDisallowDismissInThisMotion = true;
4145 }
4146
4147 @ShadeViewRefactor(RefactorComponent.INPUT)
4148 public void cancelLongPress() {
4149 mSwipeHelper.cancelLongPress();
4150 }
4151
4152 @ShadeViewRefactor(RefactorComponent.INPUT)
4153 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
4154 mOnEmptySpaceClickListener = listener;
4155 }
4156
4157 /** @hide */
4158 @Override
4159 @ShadeViewRefactor(RefactorComponent.INPUT)
4160 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4161 if (super.performAccessibilityActionInternal(action, arguments)) {
4162 return true;
4163 }
4164 if (!isEnabled()) {
4165 return false;
4166 }
4167 int direction = -1;
4168 switch (action) {
4169 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4170 // fall through
4171 case android.R.id.accessibilityActionScrollDown:
4172 direction = 1;
4173 // fall through
4174 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4175 // fall through
4176 case android.R.id.accessibilityActionScrollUp:
Gus Prevas0fa58d62019-01-11 13:58:40 -05004177 if (ANCHOR_SCROLLING) {
4178 // TODO
4179 } else {
4180 final int viewportHeight =
4181 getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
4182 - mShelf.getIntrinsicHeight();
4183 final int targetScrollY = Math.max(0,
4184 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4185 if (targetScrollY != mOwnScrollY) {
4186 mScroller.startScroll(mScrollX, mOwnScrollY, 0,
4187 targetScrollY - mOwnScrollY);
4188 animateScroll();
4189 return true;
4190 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004191 }
4192 break;
4193 }
4194 return false;
4195 }
4196
4197 @ShadeViewRefactor(RefactorComponent.INPUT)
4198 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4199 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
4200 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
4201 View translatingParentView = mSwipeHelper.getTranslatingParentView();
4202 View view = null;
4203 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4204 // Only close visible guts if they're not a leavebehind.
4205 view = guts;
4206 } else if (menuRow != null && menuRow.isMenuVisible()
4207 && translatingParentView != null) {
4208 // Checking menu
4209 view = translatingParentView;
4210 }
4211 if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
4212 // Touch was outside visible guts / menu notification, close what's visible
4213 mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
4214 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4215 false /* resetMenu */);
4216 resetExposedMenuView(true /* animate */, true /* force */);
4217 }
4218 }
4219
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05004220 @ShadeViewRefactor(RefactorComponent.INPUT)
4221 private void setSwipingInProgress(boolean swiping) {
4222 mSwipingInProgress = swiping;
4223 if (swiping) {
4224 requestDisallowInterceptTouchEvent(true);
4225 }
4226 }
4227
Selim Cinek67b22602014-03-10 15:40:16 +01004228 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004229 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01004230 public void onWindowFocusChanged(boolean hasWindowFocus) {
4231 super.onWindowFocusChanged(hasWindowFocus);
4232 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004233 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01004234 }
4235 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004236
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07004237 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004238 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04004239 public void clearChildFocus(View child) {
4240 super.clearChildFocus(child);
4241 if (mForcedScroll == child) {
4242 mForcedScroll = null;
4243 }
4244 }
4245
Selim Cinekfab078b2014-03-27 22:45:58 +01004246 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004247 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfab078b2014-03-27 22:45:58 +01004248 public boolean isScrolledToTop() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004249 if (ANCHOR_SCROLLING) {
4250 updateScrollAnchor();
4251 // TODO: once we're recycling this will need to check the adapter position of the child
4252 return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0;
4253 } else {
4254 return mOwnScrollY == 0;
4255 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004256 }
4257
4258 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004259 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004260 public boolean isScrolledToBottom() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004261 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05004262 return getMaxPositiveScrollAmount() <= 0;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004263 } else {
4264 return mOwnScrollY >= getScrollRange();
4265 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004266 }
4267
4268 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004269 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfab078b2014-03-27 22:45:58 +01004270 public View getHostView() {
4271 return this;
4272 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004273
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004274 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004275 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08004276 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004277 }
4278
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004279 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004280 public void checkSnoozeLeavebehind() {
4281 if (mCheckForLeavebehind) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04004282 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
Eliot Courtney47098cb2017-10-18 17:30:30 +09004283 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4284 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004285 mCheckForLeavebehind = false;
4286 }
4287 }
4288
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004289 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004290 public void resetCheckSnoozeLeavebehind() {
4291 mCheckForLeavebehind = true;
4292 }
4293
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004294 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004295 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004296 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004297 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004298 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02004299 }
4300
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004301 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004302 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004303 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004304 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08004305 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02004306 if (!mIsExpanded) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004307 resetScrollPosition();
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004308 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004309 clearTemporaryViews();
4310 clearUserLockedViews();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004311 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08004312 if (draggedViews.size() > 0) {
4313 draggedViews.clear();
4314 updateContinuousShadowDrawing();
4315 }
Selim Cinek5b1591a2017-07-03 17:05:01 +02004316 }
4317 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01004318
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004319 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004320 private void clearUserLockedViews() {
4321 for (int i = 0; i < getChildCount(); i++) {
4322 ExpandableView child = (ExpandableView) getChildAt(i);
4323 if (child instanceof ExpandableNotificationRow) {
4324 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4325 row.setUserLocked(false);
4326 }
4327 }
4328 }
4329
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004330 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004331 private void clearTemporaryViews() {
Selim Cinek9dd0d042018-05-14 18:12:42 -07004332 // lets make sure nothing is transient anymore
Rohan Shah8ee53652018-04-05 11:13:50 -07004333 clearTemporaryViewsInGroup(this);
Selim Cinek5b1591a2017-07-03 17:05:01 +02004334 for (int i = 0; i < getChildCount(); i++) {
4335 ExpandableView child = (ExpandableView) getChildAt(i);
4336 if (child instanceof ExpandableNotificationRow) {
4337 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Rohan Shah8ee53652018-04-05 11:13:50 -07004338 clearTemporaryViewsInGroup(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07004339 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02004340 }
Selim Cinek1685e632014-04-08 02:27:49 +02004341 }
4342
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004343 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shah8ee53652018-04-05 11:13:50 -07004344 private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07004345 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04004346 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07004347 }
4348 }
4349
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004350 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004351 public void onPanelTrackingStarted() {
4352 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004353 mAmbientState.setPanelTracking(true);
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004354 resetExposedMenuView(true /* animate */, true /* force */);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004355 }
Jason Monke59dc402018-08-16 12:05:01 -04004356
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004357 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004358 public void onPanelTrackingStopped() {
4359 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004360 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004361 }
4362
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004363 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb24e0a92015-06-09 20:17:30 -07004364 public void resetScrollPosition() {
4365 mScroller.abortAnimation();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004366 if (ANCHOR_SCROLLING) {
4367 // TODO: once we're recycling this will need to modify the adapter position instead
4368 mScrollAnchorView = getFirstChildNotGone();
4369 mScrollAnchorViewY = 0;
4370 updateOnScrollChange();
4371 } else {
4372 setOwnScrollY(0);
4373 }
Selim Cinekb24e0a92015-06-09 20:17:30 -07004374 }
4375
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004376 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek3d6ae232019-01-04 14:14:33 -08004377 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02004378 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02004379 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02004380 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinek3d6ae232019-01-04 14:14:33 -08004381 mAmbientState.setShadeExpanded(isExpanded);
4382 mStateAnimator.setShadeExpanded(isExpanded);
shawnlin3a950c32019-05-15 20:06:10 +08004383 mSwipeHelper.setIsExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02004384 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08004385 if (!mIsExpanded) {
4386 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004387 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08004388 }
Selim Cinekcab4a602014-09-03 14:47:57 +02004389 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02004390 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01004391 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02004392 }
4393 }
4394
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004395 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek98713a42015-09-21 15:47:20 +02004396 private void updateChronometers() {
4397 int childCount = getChildCount();
4398 for (int i = 0; i < childCount; i++) {
4399 updateChronometerForChild(getChildAt(i));
4400 }
4401 }
4402
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004403 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek98713a42015-09-21 15:47:20 +02004404 private void updateChronometerForChild(View child) {
4405 if (child instanceof ExpandableNotificationRow) {
4406 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4407 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02004408 }
Selim Cinek1685e632014-04-08 02:27:49 +02004409 }
4410
Jorim Jaggibe565df2014-04-28 17:51:23 +02004411 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004412 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004413 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02004414 updateScrollPositionOnExpandInBottom(view);
4415 clampScrollPosition();
Lucas Dupin60661a62018-04-12 10:50:13 -07004416 notifyHeightChangeListener(view, needsAnimation);
Selim Cinekbc243a92016-09-27 16:35:13 -07004417 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
4418 ? (ExpandableNotificationRow) view
4419 : null;
Gus Prevase2d6f042018-10-17 15:25:30 -04004420 NotificationSection firstSection = getFirstVisibleSection();
4421 ActivatableNotificationView firstVisibleChild =
4422 firstSection == null ? null : firstSection.getFirstVisibleChild();
4423 if (row != null) {
4424 if (row == firstVisibleChild
4425 || row.getNotificationParent() == firstVisibleChild) {
4426 updateAlgorithmLayoutMinHeight();
4427 }
Selim Cinekbc243a92016-09-27 16:35:13 -07004428 }
Selim Cinekb5605e52015-02-20 18:21:41 +01004429 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08004430 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01004431 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004432 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02004433 }
4434
Selim Cineka5e211b2014-08-11 17:35:48 +02004435 @Override
4436 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02004437 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02004438 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02004439 }
4440
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004441 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf7a14c02014-07-07 14:01:46 +02004442 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07004443 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02004444 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004445 // TODO: once we're recycling this will need to check the adapter position of the child
Selim Cinekcb9400a2015-06-03 16:56:13 +02004446 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08004447 if (row.isSummaryWithChildren()) {
4448 return;
4449 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02004450 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08004451 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07004452 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08004453 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07004454 }
Selim Cinekdb167372016-11-17 15:41:17 -08004455 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Gus Prevase2d6f042018-10-17 15:25:30 -04004456 NotificationSection lastSection = getLastVisibleSection();
4457 ActivatableNotificationView lastVisibleChild =
4458 lastSection == null ? null : lastSection.getLastVisibleChild();
4459 if (row != lastVisibleChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08004460 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
4461 }
4462 if (endPosition > layoutEnd) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004463 if (ANCHOR_SCROLLING) {
4464 mScrollAnchorViewY -= (endPosition - layoutEnd);
4465 updateScrollAnchor();
4466 updateOnScrollChange();
4467 } else {
4468 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
4469 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02004470 mDisallowScrollingInThisMotion = true;
4471 }
4472 }
4473 }
4474 }
4475
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004476 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggibe565df2014-04-28 17:51:23 +02004477 public void setOnHeightChangedListener(
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004478 ExpandableView.OnHeightChangedListener onHeightChangedListener) {
4479 this.mOnHeightChangedListener = onHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004480 }
4481
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004482 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02004483 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08004484 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02004485 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07004486 runAnimationFinishedRunnables();
Selim Cinek9dd0d042018-05-14 18:12:42 -07004487 clearTransient();
Selim Cinek8fc78752016-07-13 14:34:56 -07004488 clearHeadsUpDisappearRunning();
4489 }
4490
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004491 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8fc78752016-07-13 14:34:56 -07004492 private void clearHeadsUpDisappearRunning() {
4493 for (int i = 0; i < getChildCount(); i++) {
4494 View view = getChildAt(i);
4495 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07004496 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07004497 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07004498 if (row.isSummaryWithChildren()) {
4499 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004500 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07004501 }
4502 }
Selim Cinek8fc78752016-07-13 14:34:56 -07004503 }
4504 }
Selim Cinek0fccc722015-07-29 17:04:36 -07004505 }
4506
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004507 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9dd0d042018-05-14 18:12:42 -07004508 private void clearTransient() {
4509 for (ExpandableView view : mClearTransientViewsWhenFinished) {
4510 StackStateAnimator.removeTransientView(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07004511 }
Selim Cinek9dd0d042018-05-14 18:12:42 -07004512 mClearTransientViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07004513 }
4514
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004515 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek32a59fd32015-06-10 13:54:42 -07004516 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004517 for (Runnable runnable : mAnimationFinishedRunnables) {
4518 runnable.run();
4519 }
4520 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02004521 }
4522
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004523 /**
4524 * See {@link AmbientState#setDimmed}.
4525 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004526 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004527 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07004528 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004529 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02004530 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004531 mDimmedNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004532 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08004533 animateDimmed(dimmed);
4534 } else {
4535 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004536 }
4537 requestChildrenUpdate();
4538 }
4539
Selim Cinek8a9308d2017-08-24 09:31:08 -07004540 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004541 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8a9308d2017-08-24 09:31:08 -07004542 boolean isDimmed() {
4543 return mAmbientState.isDimmed();
4544 }
4545
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004546 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd35c2792016-01-21 13:20:57 -08004547 private void setDimAmount(float dimAmount) {
4548 mDimAmount = dimAmount;
4549 updateBackgroundDimming();
4550 }
4551
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004552 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd35c2792016-01-21 13:20:57 -08004553 private void animateDimmed(boolean dimmed) {
4554 if (mDimAnimator != null) {
4555 mDimAnimator.cancel();
4556 }
4557 float target = dimmed ? 1.0f : 0.0f;
4558 if (target == mDimAmount) {
4559 return;
4560 }
4561 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
4562 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
4563 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
4564 mDimAnimator.addListener(mDimEndListener);
4565 mDimAnimator.addUpdateListener(mDimUpdateListener);
4566 mDimAnimator.start();
4567 }
Evan Laird91d0f102018-09-18 17:39:55 -04004568
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004569 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Evan Laird91d0f102018-09-18 17:39:55 -04004570 private void setHideSensitive(boolean hideSensitive, boolean animate) {
Jorim Jaggiae441282014-08-01 02:45:18 +02004571 if (hideSensitive != mAmbientState.isHideSensitive()) {
4572 int childCount = getChildCount();
4573 for (int i = 0; i < childCount; i++) {
4574 ExpandableView v = (ExpandableView) getChildAt(i);
4575 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
4576 }
4577 mAmbientState.setHideSensitive(hideSensitive);
4578 if (animate && mAnimationsEnabled) {
4579 mHideSensitiveNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004580 mNeedsAnimation = true;
Jorim Jaggiae441282014-08-01 02:45:18 +02004581 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08004582 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02004583 requestChildrenUpdate();
4584 }
4585 }
4586
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004587 /**
4588 * See {@link AmbientState#setActivatedChild}.
4589 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004590 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka32ab602014-06-11 15:06:01 +02004591 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004592 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02004593 if (mAnimationsEnabled) {
4594 mActivateNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004595 mNeedsAnimation = true;
Jorim Jaggi75c95042014-05-16 19:09:59 +02004596 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004597 requestChildrenUpdate();
4598 }
4599
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004600 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka32ab602014-06-11 15:06:01 +02004601 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004602 return mAmbientState.getActivatedChild();
4603 }
4604
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004605 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02004606 private void applyCurrentState() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004607 int numChildren = getChildCount();
4608 for (int i = 0; i < numChildren; i++) {
4609 ExpandableView child = (ExpandableView) getChildAt(i);
4610 child.applyViewState();
4611 }
4612
Selim Cinekf4c19962014-05-01 21:55:31 +02004613 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004614 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02004615 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07004616 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07004617 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08004618 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08004619 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08004620 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08004621 }
4622
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004623 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08004624 private void updateViewShadows() {
4625 // we need to work around an issue where the shadow would not cast between siblings when
4626 // their z difference is between 0 and 0.1
4627
4628 // Lefts first sort by Z difference
4629 for (int i = 0; i < getChildCount(); i++) {
4630 ExpandableView child = (ExpandableView) getChildAt(i);
4631 if (child.getVisibility() != GONE) {
4632 mTmpSortedChildren.add(child);
4633 }
4634 }
4635 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
4636
4637 // Now lets update the shadow for the views
4638 ExpandableView previous = null;
4639 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
4640 ExpandableView expandableView = mTmpSortedChildren.get(i);
4641 float translationZ = expandableView.getTranslationZ();
4642 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
4643 float diff = otherZ - translationZ;
4644 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
4645 // There is no fake shadow to be drawn
4646 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
4647 } else {
4648 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07004649 expandableView.getTranslationY() - previous.getExtraBottomPadding();
4650 expandableView.setFakeShadowIntensity(
4651 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08004652 previous.getOutlineAlpha(), (int) yLocation,
4653 previous.getOutlineTranslation());
4654 }
4655 previous = expandableView;
4656 }
4657
4658 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02004659 }
4660
Lucas Dupine17ce522017-07-17 15:45:06 -07004661 /**
4662 * Update colors of "dismiss" and "empty shade" views.
4663 *
4664 * @param lightTheme True if light theme should be used.
4665 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004666 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupine17ce522017-07-17 15:45:06 -07004667 public void updateDecorViews(boolean lightTheme) {
4668 if (lightTheme == mUsingLightTheme) {
4669 return;
4670 }
4671 mUsingLightTheme = lightTheme;
4672 Context context = new ContextThemeWrapper(mContext,
4673 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
Jason Changb4e879d2018-04-11 11:17:58 +08004674 final int textColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004675 mFooterView.setTextColor(textColor);
Lucas Dupine17ce522017-07-17 15:45:06 -07004676 mEmptyShadeView.setTextColor(textColor);
4677 }
4678
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004679 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004680 public void goToFullShade(long delay) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004681 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004682 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004683 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004684 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02004685 }
4686
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004687 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02004688 public void cancelExpandHelper() {
4689 mExpandHelper.cancel();
4690 }
4691
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004692 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02004693 public void setIntrinsicPadding(int intrinsicPadding) {
4694 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07004695 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02004696 }
4697
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004698 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02004699 public int getIntrinsicPadding() {
4700 return mIntrinsicPadding;
4701 }
4702
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004703 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02004704 * @return the y position of the first notification
4705 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004706 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi457cc352014-06-02 22:47:42 +02004707 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07004708 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02004709 }
4710
Selim Cinekc0ce82d2014-06-10 13:21:15 +02004711 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004712 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc0ce82d2014-06-10 13:21:15 +02004713 public boolean shouldDelayChildPressedState() {
4714 return true;
4715 }
4716
Jorim Jaggi457cc352014-06-02 22:47:42 +02004717 /**
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004718 * See {@link AmbientState#setDozing}.
John Spurlockbf370992014-06-17 13:58:31 -04004719 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004720 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004721 public void setDozing(boolean dozing, boolean animate,
4722 @Nullable PointF touchWakeUpScreenLocation) {
4723 if (mAmbientState.isDozing() == dozing) {
Adrian Roos260c1f72017-08-07 15:52:26 +02004724 return;
4725 }
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004726 mAmbientState.setDozing(dozing);
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004727 requestChildrenUpdate();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004728 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04004729 }
4730
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004731 /**
Selim Cinek195dfc52019-05-30 19:35:05 -07004732 * Sets the current hide amount.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004733 *
Selim Cinek195dfc52019-05-30 19:35:05 -07004734 * @param linearHideAmount The hide amount that follows linear interpoloation in the
Jason Monke59dc402018-08-16 12:05:01 -04004735 * animation,
4736 * i.e. animates from 0 to 1 or vice-versa in a linear manner.
Selim Cinek195dfc52019-05-30 19:35:05 -07004737 * @param interpolatedHideAmount The hide amount that follows the actual interpolation of the
Jason Monke59dc402018-08-16 12:05:01 -04004738 * animation curve.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004739 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004740 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek195dfc52019-05-30 19:35:05 -07004741 public void setHideAmount(float linearHideAmount, float interpolatedHideAmount) {
4742 mLinearHideAmount = linearHideAmount;
4743 mInterpolatedHideAmount = interpolatedHideAmount;
4744 boolean wasFullyHidden = mAmbientState.isFullyHidden();
4745 boolean wasHiddenAtAll = mAmbientState.isHiddenAtAll();
4746 mAmbientState.setHideAmount(interpolatedHideAmount);
4747 boolean nowFullyHidden = mAmbientState.isFullyHidden();
4748 boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
4749 if (nowFullyHidden != wasFullyHidden) {
Selim Cinekd09ec7e2019-06-26 11:34:57 -07004750 updateVisibility();
Lucas Dupin16cfe452018-02-08 13:14:50 -08004751 }
Selim Cinek195dfc52019-05-30 19:35:05 -07004752 if (!wasHiddenAtAll && nowHiddenAtAll) {
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004753 resetExposedMenuView(true /* animate */, true /* animate */);
4754 }
Selim Cinek195dfc52019-05-30 19:35:05 -07004755 if (nowFullyHidden != wasFullyHidden || wasHiddenAtAll != nowHiddenAtAll) {
Lucas Dupin64e2f572019-03-21 14:21:14 -07004756 invalidateOutline();
4757 }
Lucas Dupin60661a62018-04-12 10:50:13 -07004758 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07004759 updateBackgroundDimming();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004760 requestChildrenUpdate();
Selim Cinek972123d2016-05-03 14:25:58 -07004761 }
4762
Selim Cinekd09ec7e2019-06-26 11:34:57 -07004763 private void updateVisibility() {
4764 boolean shouldShow = !mAmbientState.isFullyHidden() || !onKeyguard();
4765 setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
4766 }
4767
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004768 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek195dfc52019-05-30 19:35:05 -07004769 public void notifyHideAnimationStart(boolean hide) {
4770 // We only swap the scaling factor if we're fully hidden or fully awake to avoid
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004771 // interpolation issues when playing with the power button.
Selim Cinek195dfc52019-05-30 19:35:05 -07004772 if (mInterpolatedHideAmount == 0 || mInterpolatedHideAmount == 1) {
4773 mBackgroundXFactor = hide ? 1.8f : 1.5f;
4774 mHideXInterpolator = hide
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004775 ? Interpolators.FAST_OUT_SLOW_IN_REVERSE
4776 : Interpolators.FAST_OUT_SLOW_IN;
4777 }
Selim Cinek972123d2016-05-03 14:25:58 -07004778 }
4779
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004780 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004781 private int getNotGoneIndex(View child) {
4782 int count = getChildCount();
4783 int notGoneIndex = 0;
4784 for (int i = 0; i < count; i++) {
4785 View v = getChildAt(i);
4786 if (child == v) {
4787 return notGoneIndex;
4788 }
4789 if (v.getVisibility() != View.GONE) {
4790 notGoneIndex++;
4791 }
4792 }
4793 return -1;
4794 }
4795
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004796 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004797 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004798 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004799 if (mFooterView != null) {
4800 index = indexOfChild(mFooterView);
4801 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004802 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004803 mFooterView = footerView;
4804 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004805 }
4806
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004807 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004808 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004809 int index = -1;
4810 if (mEmptyShadeView != null) {
4811 index = indexOfChild(mEmptyShadeView);
4812 removeView(mEmptyShadeView);
4813 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004814 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004815 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004816 }
4817
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004818 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004819 public void updateEmptyShadeView(boolean visible) {
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004820 mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
Lucas Dupinc9274ff2018-05-09 17:40:20 -07004821
4822 int oldTextRes = mEmptyShadeView.getTextResource();
4823 int newTextRes = mStatusBar.areNotificationsHidden()
4824 ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
4825 if (oldTextRes != newTextRes) {
4826 mEmptyShadeView.setText(newTextRes);
4827 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004828 }
4829
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004830 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004831 public void updateFooterView(boolean visible, boolean showDismissView) {
4832 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004833 return;
4834 }
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004835 boolean animate = mIsExpanded && mAnimationsEnabled;
4836 mFooterView.setVisible(visible, animate);
4837 mFooterView.setSecondaryVisible(showDismissView, animate);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004838 }
4839
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004840 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dan Sandlereceda3d2014-07-21 15:35:01 -04004841 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4842 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004843 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004844 handleDismissAllClipping();
4845 }
4846
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004847 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek9c17b772015-07-07 20:37:09 -07004848 private void handleDismissAllClipping() {
4849 final int count = getChildCount();
4850 boolean previousChildWillBeDismissed = false;
4851 for (int i = 0; i < count; i++) {
4852 ExpandableView child = (ExpandableView) getChildAt(i);
4853 if (child.getVisibility() == GONE) {
4854 continue;
4855 }
4856 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4857 child.setMinClipTopAmount(child.getClipTopAmount());
4858 } else {
4859 child.setMinClipTopAmount(0);
4860 }
Ned Burns61269442019-05-02 18:27:23 -04004861 previousChildWillBeDismissed = StackScrollAlgorithm.canChildBeDismissed(child);
Selim Cinek9c17b772015-07-07 20:37:09 -07004862 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004863 }
4864
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004865 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004866 public boolean isFooterViewNotGone() {
4867 return mFooterView != null
4868 && mFooterView.getVisibility() != View.GONE
4869 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004870 }
4871
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004872 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek38475442018-05-18 11:11:46 -07004873 public boolean isFooterViewContentVisible() {
4874 return mFooterView != null && mFooterView.isContentVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004875 }
4876
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004877 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004878 public int getFooterViewHeight() {
4879 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004880 }
4881
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004882 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004883 public int getEmptyShadeViewHeight() {
4884 return mEmptyShadeView.getHeight();
4885 }
4886
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004887 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004888 public float getBottomMostNotificationBottom() {
4889 final int count = getChildCount();
4890 float max = 0;
4891 for (int childIdx = 0; childIdx < count; childIdx++) {
4892 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4893 if (child.getVisibility() == GONE) {
4894 continue;
4895 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004896 float bottom = child.getTranslationY() + child.getActualHeight()
4897 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004898 if (bottom > max) {
4899 max = bottom;
4900 }
4901 }
Selim Cinekd2281152015-04-10 14:37:46 -07004902 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004903 }
4904
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004905 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004906 public void setStatusBar(StatusBar statusBar) {
4907 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004908 }
4909
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004910 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb5605e52015-02-20 18:21:41 +01004911 public void setGroupManager(NotificationGroupManager groupManager) {
4912 this.mGroupManager = groupManager;
Kevin01a53cb2018-11-09 18:19:54 -08004913 mGroupManager.addOnGroupChangeListener(mOnGroupChangeListener);
Selim Cinek379ff8f2015-02-20 17:03:16 +01004914 }
4915
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004916 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek379ff8f2015-02-20 17:03:16 +01004917 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004918 if (mIsExpanded && mAnimationsEnabled) {
4919 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004920 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004921 requestChildrenUpdate();
4922 }
4923 }
4924
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004925 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek04fb2582015-06-02 19:58:09 +02004926 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004927 int childCount = getChildCount();
4928 for (int i = childCount - 1; i >= 0; i--) {
4929 ExpandableView child = (ExpandableView) getChildAt(i);
4930 if (child.getVisibility() != View.GONE) {
4931 float childTop = child.getY();
4932 if (childTop > touchY) {
4933 // we are above a notification entirely let's abort
4934 return false;
4935 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004936 boolean belowChild = touchY > childTop + child.getActualHeight()
4937 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004938 if (child == mFooterView) {
Jason Monke59dc402018-08-16 12:05:01 -04004939 if (!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
4940 touchY - childTop)) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004941 // We clicked on the dismiss button
4942 return false;
4943 }
4944 } else if (child == mEmptyShadeView) {
4945 // We arrived at the empty shade view, for which we accept all clicks
4946 return true;
Jason Monke59dc402018-08-16 12:05:01 -04004947 } else if (!belowChild) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004948 // We are on a child
4949 return false;
4950 }
4951 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004952 }
Selim Cinek04fb2582015-06-02 19:58:09 +02004953 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004954 }
4955
Selim Cinekc22fff62016-05-20 12:44:30 -07004956 /** @hide */
4957 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004958 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07004959 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
4960 super.onInitializeAccessibilityEventInternal(event);
4961 event.setScrollable(mScrollable);
4962 event.setScrollX(mScrollX);
Selim Cinekc22fff62016-05-20 12:44:30 -07004963 event.setMaxScrollX(mScrollX);
Gus Prevas0fa58d62019-01-11 13:58:40 -05004964 if (ANCHOR_SCROLLING) {
4965 // TODO
4966 } else {
4967 event.setScrollY(mOwnScrollY);
4968 event.setMaxScrollY(getScrollRange());
4969 }
Selim Cinekc22fff62016-05-20 12:44:30 -07004970 }
4971
4972 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004973 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07004974 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
4975 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07004976 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004977 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07004978 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004979 info.addAction(
4980 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
4981 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4982 }
Selim Cinekef406062016-09-29 17:33:13 -07004983 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07004984 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4985 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
4986 }
4987 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07004988 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
4989 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07004990 }
4991
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004992 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb5605e52015-02-20 18:21:41 +01004993 public void generateChildOrderChangedEvent() {
4994 if (mIsExpanded && mAnimationsEnabled) {
4995 mGenerateChildOrderChangedEvent = true;
4996 mNeedsAnimation = true;
4997 requestChildrenUpdate();
4998 }
4999 }
5000
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005001 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005002 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005003 public int getContainerChildCount() {
5004 return getChildCount();
5005 }
5006
5007 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005008 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005009 public View getContainerChildAt(int i) {
5010 return getChildAt(i);
5011 }
5012
5013 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005014 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005015 public void removeContainerView(View v) {
Ned Burnsd4a69f72019-06-19 19:49:19 -04005016 Assert.isMainThread();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005017 removeView(v);
5018 }
5019
5020 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005021 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005022 public void addContainerView(View v) {
Ned Burnsd4a69f72019-06-19 19:49:19 -04005023 Assert.isMainThread();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005024 addView(v);
5025 }
5026
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005027 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek684a4422015-04-15 16:18:39 -07005028 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005029 mAnimationFinishedRunnables.add(runnable);
5030 }
5031
Ned Burnsf81c4c42019-01-07 14:10:43 -05005032 public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
Evan Laird94492852018-10-25 13:43:01 -04005033 ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
5034 generateHeadsUpAnimation(row, isHeadsUp);
5035 }
5036
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005037 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005038 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07005039 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005040 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
5041 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07005042 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07005043 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07005044 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005045 requestChildrenUpdate();
5046 }
5047 }
5048
Selim Cineka59ecc32015-04-07 10:51:49 -07005049 /**
5050 * Set the boundary for the bottom heads up position. The heads up will always be above this
5051 * position.
5052 *
Jason Monke59dc402018-08-16 12:05:01 -04005053 * @param height the height of the screen
Selim Cineka59ecc32015-04-07 10:51:49 -07005054 * @param bottomBarHeight the height of the bar on the bottom
5055 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005056 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka59ecc32015-04-07 10:51:49 -07005057 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
5058 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
5059 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005060 requestChildrenUpdate();
5061 }
5062
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005063 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005064 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
5065 mTrackingHeadsUp = row != null;
5066 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005067 }
5068
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005069 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaac93252015-04-14 20:04:12 -07005070 public void setScrimController(ScrimController scrimController) {
5071 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07005072 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07005073 }
5074
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005075 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005076 public void forceNoOverlappingRendering(boolean force) {
5077 mForceNoOverlappingRendering = force;
5078 }
5079
5080 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005081 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005082 public boolean hasOverlappingRendering() {
5083 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
5084 }
5085
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005086 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek6811d722016-01-19 17:53:12 -08005087 public void setAnimationRunning(boolean animationRunning) {
5088 if (animationRunning != mAnimationRunning) {
5089 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07005090 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005091 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07005092 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005093 }
5094 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08005095 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08005096 }
5097 }
5098
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005099 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3776fe02016-02-04 13:32:43 -08005100 public boolean isExpanded() {
5101 return mIsExpanded;
5102 }
5103
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005104 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin4e023812018-04-02 21:19:23 -07005105 public void setPulsing(boolean pulsing, boolean animated) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09005106 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01005107 return;
5108 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005109 mPulsing = pulsing;
Selim Cinekebf42342017-07-13 15:46:10 +02005110 mAmbientState.setPulsing(pulsing);
Selim Cinekd0b48e32019-05-24 20:49:23 -07005111 mSwipeHelper.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005112 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08005113 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08005114 updateContentHeight();
Adrian Roosd83e9992017-03-16 15:17:57 -07005115 requestChildrenUpdate();
Lucas Dupin4e023812018-04-02 21:19:23 -07005116 notifyHeightChangeListener(null, animated);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005117 }
5118
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005119 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07005120 public void setQsExpanded(boolean qsExpanded) {
5121 mQsExpanded = qsExpanded;
5122 updateAlgorithmLayoutMinHeight();
Riddle Hsu065c01c2018-05-10 23:14:19 +08005123 updateScrollability();
Selim Cinekbc243a92016-09-27 16:35:13 -07005124 }
5125
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005126 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005127 public void setQsExpansionFraction(float qsExpansionFraction) {
5128 mQsExpansionFraction = qsExpansionFraction;
5129 }
5130
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005131 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Gus Prevas0fa58d62019-01-11 13:58:40 -05005132 private void setOwnScrollY(int ownScrollY) {
5133 assert !ANCHOR_SCROLLING;
Selim Cinekef406062016-09-29 17:33:13 -07005134 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08005135 // We still want to call the normal scrolled changed for accessibility reasons
5136 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07005137 mOwnScrollY = ownScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -05005138 updateOnScrollChange();
5139 }
5140 }
5141
5142 private void updateOnScrollChange() {
5143 updateForwardAndBackwardScrollability();
5144 requestChildrenUpdate();
5145 }
5146
5147 private void updateScrollAnchor() {
5148 int anchorIndex = indexOfChild(mScrollAnchorView);
5149 // If the anchor view has been scrolled off the top, move to the next view.
5150 while (mScrollAnchorViewY < 0) {
5151 View nextAnchor = null;
5152 for (int i = anchorIndex + 1; i < getChildCount(); i++) {
5153 View child = getChildAt(i);
5154 if (child.getVisibility() != View.GONE
5155 && child instanceof ExpandableNotificationRow) {
5156 anchorIndex = i;
5157 nextAnchor = child;
5158 break;
5159 }
5160 }
5161 if (nextAnchor == null) {
5162 break;
5163 }
5164 mScrollAnchorViewY +=
5165 (int) (nextAnchor.getTranslationY() - mScrollAnchorView.getTranslationY());
5166 mScrollAnchorView = nextAnchor;
5167 }
5168 // If the view above the anchor view is fully visible, make it the anchor view.
5169 while (anchorIndex > 0 && mScrollAnchorViewY > 0) {
5170 View prevAnchor = null;
5171 for (int i = anchorIndex - 1; i >= 0; i--) {
5172 View child = getChildAt(i);
5173 if (child.getVisibility() != View.GONE
5174 && child instanceof ExpandableNotificationRow) {
5175 anchorIndex = i;
5176 prevAnchor = child;
5177 break;
5178 }
5179 }
5180 if (prevAnchor == null) {
5181 break;
5182 }
5183 float distanceToPreviousAnchor =
5184 mScrollAnchorView.getTranslationY() - prevAnchor.getTranslationY();
5185 if (distanceToPreviousAnchor < mScrollAnchorViewY) {
5186 mScrollAnchorViewY -= (int) distanceToPreviousAnchor;
5187 mScrollAnchorView = prevAnchor;
5188 }
Selim Cinekef406062016-09-29 17:33:13 -07005189 }
5190 }
5191
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005192 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005193 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07005194 int index = -1;
5195 if (mShelf != null) {
5196 index = indexOfChild(mShelf);
5197 removeView(mShelf);
5198 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08005199 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07005200 addView(mShelf, index);
5201 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07005202 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07005203 shelf.bind(mAmbientState, this);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005204 if (ANCHOR_SCROLLING) {
5205 mScrollAnchorView = mShelf;
5206 }
Selim Cinek281c2022016-10-13 19:14:43 -07005207 }
5208
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005209 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005210 public NotificationShelf getNotificationShelf() {
5211 return mShelf;
5212 }
5213
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005214 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekad7fac02016-10-18 17:09:15 -07005215 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
5216 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
5217 mMaxDisplayedNotifications = maxDisplayedNotifications;
5218 updateContentHeight();
5219 notifyHeightChangeListener(mShelf);
5220 }
5221 }
5222
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005223 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005224 public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
Jason Monke59dc402018-08-16 12:05:01 -04005225 mShouldShowShelfOnly = shouldShowShelfOnly;
shawnlin8e4e92c2018-04-12 18:47:24 +08005226 updateAlgorithmLayoutMinHeight();
5227 }
5228
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005229 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek48ff9b42016-11-09 19:31:51 -08005230 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07005231 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08005232 }
5233
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005234 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005235 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
5236 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
5237 updateClipping();
5238 }
5239
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005240 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005241 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
5242 mHeadsUpAnimatingAway = headsUpAnimatingAway;
5243 updateClipping();
5244 }
5245
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005246 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk297c04e2018-08-23 17:16:59 -04005247 @VisibleForTesting
5248 protected void setStatusBarState(int statusBarState) {
Selim Cinek355652a2016-12-07 13:32:12 -08005249 mStatusBarState = statusBarState;
5250 mAmbientState.setStatusBarState(statusBarState);
Evan Laird91d0f102018-09-18 17:39:55 -04005251 }
5252
5253 private void onStatePostChange() {
Jason Monk1fd3fc32018-08-14 17:20:09 -04005254 boolean onKeyguard = onKeyguard();
5255 boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
Evan Laird91d0f102018-09-18 17:39:55 -04005256
Jason Monk1fd3fc32018-08-14 17:20:09 -04005257 if (mHeadsUpAppearanceController != null) {
Selim Cinekb2c5dc52019-06-24 15:46:52 -07005258 mHeadsUpAppearanceController.onStateChanged();
Jason Monk1fd3fc32018-08-14 17:20:09 -04005259 }
5260
Beverly8fdb5332019-02-04 14:29:49 -05005261 SysuiStatusBarStateController state = (SysuiStatusBarStateController)
5262 Dependency.get(StatusBarStateController.class);
Jason Monk1fd3fc32018-08-14 17:20:09 -04005263 setHideSensitive(publicMode, state.goingToFullShade() /* animate */);
5264 setDimmed(onKeyguard, state.fromShadeLocked() /* animate */);
5265 setExpandingEnabled(!onKeyguard);
5266 ActivatableNotificationView activatedChild = getActivatedChild();
5267 setActivatedChild(null);
5268 if (activatedChild != null) {
5269 activatedChild.makeInactive(false /* animate */);
5270 }
Jason Monke59dc402018-08-16 12:05:01 -04005271 updateFooter();
Dave Mankoff57445802018-10-10 14:47:34 -04005272 requestChildrenUpdate();
Jason Monke59dc402018-08-16 12:05:01 -04005273 onUpdateRowStates();
Evan Laird91d0f102018-09-18 17:39:55 -04005274
5275 mEntryManager.updateNotifications();
Selim Cinekd09ec7e2019-06-26 11:34:57 -07005276 updateVisibility();
Selim Cinek355652a2016-12-07 13:32:12 -08005277 }
5278
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005279 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd5ab6452016-12-08 16:34:00 -08005280 public void setExpandingVelocity(float expandingVelocity) {
5281 mAmbientState.setExpandingVelocity(expandingVelocity);
5282 }
5283
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005284 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005285 public float getOpeningHeight() {
5286 if (mEmptyShadeView.getVisibility() == GONE) {
5287 return getMinExpansionHeight();
5288 } else {
5289 return getAppearEndPosition();
5290 }
5291 }
5292
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005293 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005294 public void setIsFullWidth(boolean isFullWidth) {
5295 mAmbientState.setPanelFullWidth(isFullWidth);
5296 }
5297
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005298 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekec29d342017-05-05 18:31:49 -07005299 public void setUnlockHintRunning(boolean running) {
5300 mAmbientState.setUnlockHintRunning(running);
5301 }
5302
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005303 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005304 public void setQsCustomizerShowing(boolean isShowing) {
5305 mAmbientState.setQsCustomizerShowing(isShowing);
5306 requestChildrenUpdate();
5307 }
5308
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005309 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005310 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
5311 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
5312 }
5313
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005314 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek707e2072017-06-30 18:32:40 +02005315 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5316 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
shawnlin8e4e92c2018-04-12 18:47:24 +08005317 + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
5318 + " qsExpandFraction=%f]",
Selim Cinek707e2072017-06-30 18:32:40 +02005319 this.getClass().getSimpleName(),
Jason Monke59dc402018-08-16 12:05:01 -04005320 mPulsing ? "T" : "f",
5321 mAmbientState.isQsCustomizerShowing() ? "T" : "f",
Selim Cinek707e2072017-06-30 18:32:40 +02005322 getVisibility() == View.VISIBLE ? "visible"
5323 : getVisibility() == View.GONE ? "gone"
5324 : "invisible",
5325 getAlpha(),
shawnlin8e4e92c2018-04-12 18:47:24 +08005326 mAmbientState.getScrollY(),
5327 mMaxTopPadding,
Jason Monke59dc402018-08-16 12:05:01 -04005328 mShouldShowShelfOnly ? "T" : "f",
shawnlin8e4e92c2018-04-12 18:47:24 +08005329 mQsExpansionFraction));
Selim Cinek30887662018-10-15 17:37:21 -07005330 int childCount = getChildCount();
5331 pw.println(" Number of children: " + childCount);
5332 pw.println();
5333
5334 for (int i = 0; i < childCount; i++) {
5335 ExpandableView child = (ExpandableView) getChildAt(i);
5336 child.dump(fd, pw, args);
5337 if (!(child instanceof ExpandableNotificationRow)) {
5338 pw.println(" " + child.getClass().getSimpleName());
5339 // Notifications dump it's viewstate as part of their dump to support children
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005340 ExpandableViewState viewState = child.getViewState();
Selim Cinek30887662018-10-15 17:37:21 -07005341 if (viewState == null) {
5342 pw.println(" no viewState!!!");
5343 } else {
5344 pw.print(" ");
5345 viewState.dump(fd, pw, args);
5346 pw.println();
5347 pw.println();
5348 }
5349 }
5350 }
Selim Cinek30887662018-10-15 17:37:21 -07005351 int transientViewCount = getTransientViewCount();
Selim Cinekd4c32302018-11-19 19:43:14 -08005352 pw.println(" Transient Views: " + transientViewCount);
Selim Cinek30887662018-10-15 17:37:21 -07005353 for (int i = 0; i < transientViewCount; i++) {
5354 ExpandableView child = (ExpandableView) getTransientView(i);
5355 child.dump(fd, pw, args);
5356 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005357 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08005358 int draggedCount = draggedViews.size();
5359 pw.println(" Dragged Views: " + draggedCount);
5360 for (int i = 0; i < draggedCount; i++) {
5361 ExpandableView child = (ExpandableView) draggedViews.get(i);
5362 child.dump(fd, pw, args);
5363 }
Selim Cinek707e2072017-06-30 18:32:40 +02005364 }
5365
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005366 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek195dfc52019-05-30 19:35:05 -07005367 public boolean isFullyHidden() {
5368 return mAmbientState.isFullyHidden();
Lucas Dupin16cfe452018-02-08 13:14:50 -08005369 }
5370
Selim Cinek7103fd42016-05-09 22:22:33 -04005371 /**
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005372 * Add a listener whenever the expanded height changes. The first value passed as an
5373 * argument is the expanded height and the second one is the appearFraction.
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005374 *
5375 * @param listener the listener to notify.
5376 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005377 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005378 public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005379 mExpandedHeightListeners.add(listener);
5380 }
5381
5382 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07005383 * Stop a listener from listening to the expandedHeight.
5384 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005385 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005386 public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
Selim Cinek60ffea62018-03-22 13:16:44 -07005387 mExpandedHeightListeners.remove(listener);
5388 }
5389
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005390 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekf0c79e12018-05-14 17:17:31 -07005391 public void setHeadsUpAppearanceController(
5392 HeadsUpAppearanceController headsUpAppearanceController) {
5393 mHeadsUpAppearanceController = headsUpAppearanceController;
5394 }
5395
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005396 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek9bfc7a52018-06-11 16:09:00 -07005397 public void setIconAreaController(NotificationIconAreaController controller) {
5398 mIconAreaController = controller;
5399 }
5400
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005401 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005402 public void manageNotifications(View v) {
5403 Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
5404 mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
5405 }
5406
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005407 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -04005408 private void clearNotifications(
5409 @SelectedRows int selection,
5410 boolean closeShade) {
Jason Monke59dc402018-08-16 12:05:01 -04005411 // animate-swipe all dismissable notifications, then animate the shade closed
5412 int numChildren = getChildCount();
5413
5414 final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
5415 final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
5416 for (int i = 0; i < numChildren; i++) {
5417 final View child = getChildAt(i);
5418 if (child instanceof ExpandableNotificationRow) {
5419 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
5420 boolean parentVisible = false;
5421 boolean hasClipBounds = child.getClipBounds(mTmpRect);
Ned Burns61269442019-05-02 18:27:23 -04005422 if (includeChildInDismissAll(row, selection)) {
Jason Monke59dc402018-08-16 12:05:01 -04005423 viewsToRemove.add(row);
5424 if (child.getVisibility() == View.VISIBLE
5425 && (!hasClipBounds || mTmpRect.height() > 0)) {
5426 viewsToHide.add(child);
5427 parentVisible = true;
5428 }
5429 } else if (child.getVisibility() == View.VISIBLE
5430 && (!hasClipBounds || mTmpRect.height() > 0)) {
5431 parentVisible = true;
5432 }
5433 List<ExpandableNotificationRow> children = row.getNotificationChildren();
5434 if (children != null) {
5435 for (ExpandableNotificationRow childRow : children) {
Ned Burns61269442019-05-02 18:27:23 -04005436 if (includeChildInDismissAll(row, selection)) {
5437 viewsToRemove.add(childRow);
5438 if (parentVisible && row.areChildrenExpanded()) {
5439 hasClipBounds = childRow.getClipBounds(mTmpRect);
5440 if (childRow.getVisibility() == View.VISIBLE
5441 && (!hasClipBounds || mTmpRect.height() > 0)) {
5442 viewsToHide.add(childRow);
5443 }
Jason Monke59dc402018-08-16 12:05:01 -04005444 }
5445 }
5446 }
5447 }
5448 }
5449 }
Ned Burns61269442019-05-02 18:27:23 -04005450
Jason Monke59dc402018-08-16 12:05:01 -04005451 if (viewsToRemove.isEmpty()) {
Ned Burns61269442019-05-02 18:27:23 -04005452 if (closeShade) {
5453 mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
5454 }
Jason Monke59dc402018-08-16 12:05:01 -04005455 return;
5456 }
5457
Ned Burns61269442019-05-02 18:27:23 -04005458 performDismissAllAnimations(viewsToHide, closeShade, () -> {
Jason Monke59dc402018-08-16 12:05:01 -04005459 for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
Ned Burns61269442019-05-02 18:27:23 -04005460 if (StackScrollAlgorithm.canChildBeDismissed(rowToRemove)) {
5461 if (selection == ROWS_ALL) {
5462 // TODO: This is a listener method; we shouldn't be calling it. Can we just
5463 // call performRemoveNotification as below?
5464 mEntryManager.removeNotification(
5465 rowToRemove.getEntry().key,
5466 null /* ranking */,
5467 NotificationListenerService.REASON_CANCEL_ALL);
5468 } else {
5469 mEntryManager.performRemoveNotification(
5470 rowToRemove.getEntry().notification,
5471 NotificationListenerService.REASON_CANCEL_ALL);
5472 }
Jason Monke59dc402018-08-16 12:05:01 -04005473 } else {
5474 rowToRemove.resetTranslation();
5475 }
5476 }
Ned Burns61269442019-05-02 18:27:23 -04005477 if (selection == ROWS_ALL) {
5478 try {
5479 mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
5480 } catch (Exception ex) {
5481 }
Jason Monke59dc402018-08-16 12:05:01 -04005482 }
5483 });
Jason Monke59dc402018-08-16 12:05:01 -04005484 }
5485
Ned Burns61269442019-05-02 18:27:23 -04005486 private boolean includeChildInDismissAll(
5487 ExpandableNotificationRow row,
5488 @SelectedRows int selection) {
5489 return StackScrollAlgorithm.canChildBeDismissed(row) && matchesSelection(row, selection);
5490 }
5491
5492 /**
5493 * Given a list of rows, animates them away in a staggered fashion as if they were dismissed.
5494 * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete
5495 * handler.
5496 *
5497 * @param hideAnimatedList List of rows to animated away. Should only be views that are
5498 * currently visible, or else the stagger will look funky.
5499 * @param closeShade Whether to close the shade after the stagger animation completes.
5500 * @param onAnimationComplete Called after the entire animation completes (including the shade
5501 * closing if appropriate). The rows must be dismissed for real here.
5502 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005503 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -04005504 private void performDismissAllAnimations(
5505 final ArrayList<View> hideAnimatedList,
5506 final boolean closeShade,
5507 final Runnable onAnimationComplete) {
5508
5509 final Runnable onSlideAwayAnimationComplete = () -> {
5510 if (closeShade) {
5511 mShadeController.addPostCollapseAction(() -> {
5512 setDismissAllInProgress(false);
5513 onAnimationComplete.run();
5514 });
5515 mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
5516 } else {
5517 setDismissAllInProgress(false);
5518 onAnimationComplete.run();
5519 }
Jason Monke59dc402018-08-16 12:05:01 -04005520 };
5521
5522 if (hideAnimatedList.isEmpty()) {
Ned Burns61269442019-05-02 18:27:23 -04005523 onSlideAwayAnimationComplete.run();
Jason Monke59dc402018-08-16 12:05:01 -04005524 return;
5525 }
5526
5527 // let's disable our normal animations
5528 setDismissAllInProgress(true);
5529
5530 // Decrease the delay for every row we animate to give the sense of
5531 // accelerating the swipes
5532 int rowDelayDecrement = 10;
5533 int currentDelay = 140;
5534 int totalDelay = 180;
5535 int numItems = hideAnimatedList.size();
5536 for (int i = numItems - 1; i >= 0; i--) {
5537 View view = hideAnimatedList.get(i);
5538 Runnable endRunnable = null;
5539 if (i == 0) {
Ned Burns61269442019-05-02 18:27:23 -04005540 endRunnable = onSlideAwayAnimationComplete;
Jason Monke59dc402018-08-16 12:05:01 -04005541 }
Lucas Dupinfb8bdbb2018-12-02 15:09:37 -08005542 dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
Jason Monke59dc402018-08-16 12:05:01 -04005543 currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
5544 totalDelay += currentDelay;
5545 }
5546 }
5547
5548 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005549 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005550 protected void inflateFooterView() {
5551 FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
5552 R.layout.status_bar_notification_footer, this, false);
5553 footerView.setDismissButtonClickListener(v -> {
5554 mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
Ned Burns61269442019-05-02 18:27:23 -04005555 clearNotifications(ROWS_ALL, true /* closeShade */);
Jason Monke59dc402018-08-16 12:05:01 -04005556 });
5557 footerView.setManageButtonClickListener(this::manageNotifications);
5558 setFooterView(footerView);
5559 }
5560
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005561 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5562 private void inflateEmptyShadeView() {
Jason Monke59dc402018-08-16 12:05:01 -04005563 EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
5564 R.layout.status_bar_no_notifications, this, false);
5565 view.setText(R.string.empty_shade_text);
5566 setEmptyShadeView(view);
5567 }
5568
5569 /**
5570 * Updates expanded, dimmed and locked states of notification rows.
5571 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005572 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jason Monke59dc402018-08-16 12:05:01 -04005573 public void onUpdateRowStates() {
5574 changeViewPosition(mFooterView, -1);
5575
5576 // The following views will be moved to the end of mStackScroller. This counter represents
5577 // the offset from the last child. Initialized to 1 for the very last position. It is post-
5578 // incremented in the following "changeViewPosition" calls so that its value is correct for
5579 // subsequent calls.
5580 int offsetFromEnd = 1;
5581 changeViewPosition(mEmptyShadeView,
5582 getChildCount() - offsetFromEnd++);
5583
5584 // No post-increment for this call because it is the last one. Make sure to add one if
5585 // another "changeViewPosition" call is ever added.
5586 changeViewPosition(mShelf,
5587 getChildCount() - offsetFromEnd);
Jason Monke59dc402018-08-16 12:05:01 -04005588 }
5589
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005590 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5591 public void setNotificationPanel(NotificationPanelView notificationPanelView) {
Jason Monke59dc402018-08-16 12:05:01 -04005592 mNotificationPanel = notificationPanelView;
5593 }
5594
Jason Monk297c04e2018-08-23 17:16:59 -04005595 public void updateIconAreaViews() {
5596 mIconAreaController.updateNotificationIcons();
5597 }
5598
Selim Cinek60ffea62018-03-22 13:16:44 -07005599 /**
Selim Cinek3d6ae232019-01-04 14:14:33 -08005600 * Set how far the wake up is when waking up from pulsing. This is a height and will adjust the
5601 * notification positions accordingly.
5602 * @param height the new wake up height
5603 * @return the overflow how much the height is further than he lowest notification
5604 */
Selim Cinek5040f2e2019-02-14 18:22:42 -08005605 public float setPulseHeight(float height) {
5606 mAmbientState.setPulseHeight(height);
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005607 if (mKeyguardBypassController.getBypassEnabled()) {
5608 notifyAppearChangedListeners();
5609 }
Selim Cinek3d6ae232019-01-04 14:14:33 -08005610 requestChildrenUpdate();
5611 return Math.max(0, height - mAmbientState.getInnerHeight(true /* ignorePulseHeight */));
5612 }
5613
Selim Cinekb0fada62019-06-17 19:03:59 -07005614 public float getPulseHeight() {
5615 return mAmbientState.getPulseHeight();
5616 }
5617
Selim Cinek3d6ae232019-01-04 14:14:33 -08005618 /**
Selim Cinek195dfc52019-05-30 19:35:05 -07005619 * 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 -08005620 * the notification is pulsing.
5621 */
5622 public void setDozeAmount(float dozeAmount) {
5623 mAmbientState.setDozeAmount(dozeAmount);
Selim Cinekae55d832019-02-22 17:43:43 -08005624 updateContinuousBackgroundDrawing();
Selim Cinek3d6ae232019-01-04 14:14:33 -08005625 requestChildrenUpdate();
5626 }
5627
Selim Cinek459aee32019-02-20 11:18:56 -08005628 public void wakeUpFromPulse() {
Selim Cinekb0fada62019-06-17 19:03:59 -07005629 setPulseHeight(getWakeUpHeight());
Selim Cinek459aee32019-02-20 11:18:56 -08005630 // Let's place the hidden views at the end of the pulsing notification to make sure we have
5631 // a smooth animation
5632 boolean firstVisibleView = true;
5633 float wakeUplocation = -1f;
5634 int childCount = getChildCount();
5635 for (int i = 0; i < childCount; i++) {
5636 ExpandableView view = (ExpandableView) getChildAt(i);
5637 if (view.getVisibility() == View.GONE) {
5638 continue;
5639 }
5640 boolean isShelf = view == mShelf;
5641 if (!(view instanceof ExpandableNotificationRow) && !isShelf) {
5642 continue;
5643 }
5644 if (view.getVisibility() == View.VISIBLE && !isShelf) {
5645 if (firstVisibleView) {
5646 firstVisibleView = false;
5647 wakeUplocation = view.getTranslationY()
5648 + view.getActualHeight() - mShelf.getIntrinsicHeight();
5649 }
5650 } else if (!firstVisibleView) {
5651 view.setTranslationY(wakeUplocation);
5652 }
5653 }
Selim Cinekf434a742019-05-28 17:39:49 -07005654 mDimmedNeedsAnimation = true;
Selim Cinek459aee32019-02-20 11:18:56 -08005655 }
5656
Selim Cinek6f0a62a2019-04-09 18:40:12 -07005657 @Override
5658 public void onDynamicPrivacyChanged() {
5659 if (mIsExpanded) {
5660 // The bottom might change because we're using the final actual height of the view
5661 mAnimateBottomOnLayout = true;
5662 }
5663 }
5664
Selim Cinekb0fada62019-06-17 19:03:59 -07005665 public void setOnPulseHeightChangedListener(Runnable listener) {
5666 mAmbientState.setOnPulseHeightChangedListener(listener);
5667 }
5668
Selim Cinekc7e4cb52019-06-20 15:41:45 -07005669 public float calculateAppearFractionBypass() {
5670 float pulseHeight = getPulseHeight();
5671 float wakeUpHeight = getWakeUpHeight();
5672 float dragDownAmount = pulseHeight - wakeUpHeight;
5673
5674 // The total distance required to fully reveal the header
5675 float totalDistance = getIntrinsicPadding();
5676 return MathUtils.smoothStep(0, totalDistance, dragDownAmount);
5677 }
5678
Selim Cinek3d6ae232019-01-04 14:14:33 -08005679 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005680 * A listener that is notified when the empty space below the notifications is clicked on
5681 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005682 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005683 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08005684 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005685 }
5686
5687 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02005688 * A listener that gets notified when the overscroll at the top has changed.
5689 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005690 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi290600a2014-05-30 17:02:20 +02005691 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02005692
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005693 /**
5694 * Notifies a listener that the overscroll has changed.
5695 *
5696 * @param amount the amount of overscroll, in pixels
5697 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
5698 * unrubberbanded motion to directly expand overscroll view (e.g
5699 * expand
5700 * QS)
5701 */
5702 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02005703
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005704 /**
5705 * Notify a listener that the scroller wants to escape from the scrolling motion and
5706 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
5707 *
5708 * @param velocity The velocity that the Scroller had when over flinging
5709 * @param open Should the fling open or close the overscroll view.
5710 */
5711 void flingTopOverscroll(float velocity, boolean open);
5712 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02005713
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005714 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5715 public boolean hasActiveNotifications() {
Jason Monke59dc402018-08-16 12:05:01 -04005716 return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
5717 }
5718
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04005719 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005720 public void updateSpeedBumpIndex() {
Jason Monke59dc402018-08-16 12:05:01 -04005721 int speedBumpIndex = 0;
5722 int currentIndex = 0;
5723 final int N = getChildCount();
5724 for (int i = 0; i < N; i++) {
5725 View view = getChildAt(i);
5726 if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
5727 continue;
5728 }
5729 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
5730 currentIndex++;
Gus Prevas33619af2018-10-26 15:40:27 -04005731 boolean beforeSpeedBump;
Selim Cinekda330ff2019-06-11 15:41:28 -07005732 if (mHighPriorityBeforeSpeedBump) {
Gus Prevascaed15c2019-01-18 14:19:51 -05005733 beforeSpeedBump = row.getEntry().isHighPriority();
Selim Cinekda330ff2019-06-11 15:41:28 -07005734 } else {
5735 beforeSpeedBump = !row.getEntry().ambient;
Gus Prevas33619af2018-10-26 15:40:27 -04005736 }
5737 if (beforeSpeedBump) {
Jason Monke59dc402018-08-16 12:05:01 -04005738 speedBumpIndex = currentIndex;
5739 }
5740 }
5741 boolean noAmbient = speedBumpIndex == N;
5742 updateSpeedBumpIndex(speedBumpIndex, noAmbient);
5743 }
5744
Gus Prevase2d6f042018-10-17 15:25:30 -04005745 /** Updates the indices of the boundaries between sections. */
5746 @ShadeViewRefactor(RefactorComponent.INPUT)
5747 public void updateSectionBoundaries() {
Ned Burns9eb06332019-04-23 16:02:12 -04005748 mSectionsManager.updateSectionBoundaries();
Gus Prevase2d6f042018-10-17 15:25:30 -04005749 }
5750
Selim Cinekae55d832019-02-22 17:43:43 -08005751 private void updateContinuousBackgroundDrawing() {
5752 boolean continuousBackground = !mAmbientState.isFullyAwake()
5753 && !mAmbientState.getDraggedViews().isEmpty();
5754 if (continuousBackground != mContinuousBackgroundUpdate) {
5755 mContinuousBackgroundUpdate = continuousBackground;
5756 if (continuousBackground) {
5757 getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater);
5758 } else {
5759 getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater);
5760 }
5761 }
5762 }
5763
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005764 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08005765 private void updateContinuousShadowDrawing() {
5766 boolean continuousShadowUpdate = mAnimationRunning
5767 || !mAmbientState.getDraggedViews().isEmpty();
5768 if (continuousShadowUpdate != mContinuousShadowUpdate) {
5769 if (continuousShadowUpdate) {
5770 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
5771 } else {
5772 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
5773 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07005774 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08005775 }
5776 }
5777
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005778 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005779 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -08005780 public void resetExposedMenuView(boolean animate, boolean force) {
5781 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07005782 }
5783
Ned Burns61269442019-05-02 18:27:23 -04005784 private static boolean matchesSelection(
5785 ExpandableNotificationRow row,
5786 @SelectedRows int selection) {
5787 switch (selection) {
5788 case ROWS_ALL:
5789 return true;
5790 case ROWS_HIGH_PRIORITY:
5791 return row.getEntry().isHighPriority();
5792 case ROWS_GENTLE:
5793 return !row.getEntry().isHighPriority();
5794 default:
5795 throw new IllegalArgumentException("Unknown selection: " + selection);
5796 }
5797 }
5798
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005799 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
5800 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02005801
Jason Monke59dc402018-08-16 12:05:01 -04005802 static AnimationFilter[] FILTERS = new AnimationFilter[]{
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005803
5804 // ANIMATION_TYPE_ADD
5805 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005806 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005807 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005808 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005809 .animateZ()
5810 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005811
5812 // ANIMATION_TYPE_REMOVE
5813 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005814 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005815 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005816 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005817 .animateZ()
5818 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005819
5820 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5821 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005822 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005823 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005824 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005825 .animateZ()
5826 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005827
5828 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5829 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005830 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005831 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005832 .animateY()
5833 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005834 .animateZ(),
5835
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005836 // ANIMATION_TYPE_ACTIVATED_CHILD
5837 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005838 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005839
5840 // ANIMATION_TYPE_DIMMED
5841 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005842 .animateDimmed(),
5843
5844 // ANIMATION_TYPE_CHANGE_POSITION
5845 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005846 .animateAlpha() // maybe the children change positions
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005847 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005848 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005849 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04005850 .animateZ(),
5851
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005852 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5853 new AnimationFilter()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005854 .animateHeight()
5855 .animateTopInset()
5856 .animateY()
5857 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02005858 .animateZ()
5859 .hasDelays(),
5860
5861 // ANIMATION_TYPE_HIDE_SENSITIVE
5862 new AnimationFilter()
5863 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02005864
5865 // ANIMATION_TYPE_VIEW_RESIZE
5866 new AnimationFilter()
Selim Cineka5e211b2014-08-11 17:35:48 +02005867 .animateHeight()
5868 .animateTopInset()
5869 .animateY()
5870 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02005871
Selim Cinekb5605e52015-02-20 18:21:41 +01005872 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
5873 new AnimationFilter()
5874 .animateAlpha()
5875 .animateHeight()
5876 .animateTopInset()
5877 .animateY()
5878 .animateZ(),
5879
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005880 // ANIMATION_TYPE_HEADS_UP_APPEAR
5881 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005882 .animateHeight()
5883 .animateTopInset()
5884 .animateY()
5885 .animateZ(),
5886
5887 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5888 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005889 .animateHeight()
5890 .animateTopInset()
5891 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07005892 .animateZ()
5893 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005894
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005895 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5896 new AnimationFilter()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005897 .animateHeight()
5898 .animateTopInset()
5899 .animateY()
5900 .animateZ()
5901 .hasDelays(),
5902
Selim Cineka59ecc32015-04-07 10:51:49 -07005903 // ANIMATION_TYPE_HEADS_UP_OTHER
5904 new AnimationFilter()
Selim Cineka59ecc32015-04-07 10:51:49 -07005905 .animateHeight()
5906 .animateTopInset()
5907 .animateY()
5908 .animateZ(),
5909
Selim Cinekd9acca52014-09-01 22:33:25 +02005910 // ANIMATION_TYPE_EVERYTHING
5911 new AnimationFilter()
5912 .animateAlpha()
Selim Cinekd9acca52014-09-01 22:33:25 +02005913 .animateDimmed()
5914 .animateHideSensitive()
5915 .animateHeight()
5916 .animateTopInset()
5917 .animateY()
5918 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005919 };
5920
Jason Monke59dc402018-08-16 12:05:01 -04005921 static int[] LENGTHS = new int[]{
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005922
5923 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005924 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005925
5926 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005927 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005928
5929 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5930 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5931
5932 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5933 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5934
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005935 // ANIMATION_TYPE_ACTIVATED_CHILD
5936 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
5937
5938 // ANIMATION_TYPE_DIMMED
5939 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005940
5941 // ANIMATION_TYPE_CHANGE_POSITION
5942 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04005943
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005944 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5945 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02005946
5947 // ANIMATION_TYPE_HIDE_SENSITIVE
5948 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02005949
5950 // ANIMATION_TYPE_VIEW_RESIZE
5951 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02005952
Selim Cinekb5605e52015-02-20 18:21:41 +01005953 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08005954 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01005955
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005956 // ANIMATION_TYPE_HEADS_UP_APPEAR
5957 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
5958
5959 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5960 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5961
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005962 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5963 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
5964
Selim Cineka59ecc32015-04-07 10:51:49 -07005965 // ANIMATION_TYPE_HEADS_UP_OTHER
5966 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5967
Selim Cinekd9acca52014-09-01 22:33:25 +02005968 // ANIMATION_TYPE_EVERYTHING
5969 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005970 };
5971
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005972 static final int ANIMATION_TYPE_ADD = 0;
5973 static final int ANIMATION_TYPE_REMOVE = 1;
5974 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
5975 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
Selim Cinekff2ffec2018-11-19 18:52:01 -08005976 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 4;
5977 static final int ANIMATION_TYPE_DIMMED = 5;
5978 static final int ANIMATION_TYPE_CHANGE_POSITION = 6;
Selim Cinekc3fec682019-06-06 18:11:07 -07005979 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 7;
5980 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 8;
5981 static final int ANIMATION_TYPE_VIEW_RESIZE = 9;
5982 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 10;
5983 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 11;
5984 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 12;
5985 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 13;
5986 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 14;
5987 static final int ANIMATION_TYPE_EVERYTHING = 15;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02005988
Selim Cinek572bbd42014-04-25 16:43:27 +02005989 final long eventStartTime;
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005990 final ExpandableView mChangingView;
Selim Cinek572bbd42014-04-25 16:43:27 +02005991 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005992 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005993 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005994 View viewAfterChangingView;
Selim Cineka59ecc32015-04-07 10:51:49 -07005995 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02005996
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005997 AnimationEvent(ExpandableView view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02005998 this(view, type, LENGTHS[type]);
5999 }
6000
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006001 AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006002 this(view, type, LENGTHS[type], filter);
6003 }
6004
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006005 AnimationEvent(ExpandableView view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006006 this(view, type, length, FILTERS[type]);
6007 }
6008
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006009 AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02006010 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006011 mChangingView = view;
Selim Cinek572bbd42014-04-25 16:43:27 +02006012 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006013 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07006014 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006015 }
6016
6017 /**
6018 * Combines the length of several animation events into a single value.
6019 *
6020 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006021 * @return The combined length. Depending on the event types, this might be the maximum of
Jason Monke59dc402018-08-16 12:05:01 -04006022 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006023 */
6024 static long combineLength(ArrayList<AnimationEvent> events) {
6025 long length = 0;
6026 int size = events.size();
6027 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006028 AnimationEvent event = events.get(i);
6029 length = Math.max(length, event.length);
6030 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
6031 return event.length;
6032 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006033 }
6034 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02006035 }
6036 }
Jason Monke59dc402018-08-16 12:05:01 -04006037
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006038 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
6039 private final StateListener mStateListener = new StateListener() {
Jason Monke59dc402018-08-16 12:05:01 -04006040 @Override
6041 public void onStatePreChange(int oldState, int newState) {
6042 if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
6043 requestAnimateEverything();
6044 }
6045 }
6046
6047 @Override
6048 public void onStateChanged(int newState) {
6049 setStatusBarState(newState);
6050 }
Evan Laird91d0f102018-09-18 17:39:55 -04006051
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006052 @Override
6053 public void onStatePostChange() {
Evan Laird91d0f102018-09-18 17:39:55 -04006054 NotificationStackScrollLayout.this.onStatePostChange();
6055 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006056 };
6057
Will Brockmane718d582019-01-17 16:38:38 -05006058 @VisibleForTesting
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006059 @ShadeViewRefactor(RefactorComponent.INPUT)
Will Brockmane718d582019-01-17 16:38:38 -05006060 protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006061 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006062 public void onMenuClicked(View view, int x, int y, MenuItem item) {
6063 if (mLongPressListener == null) {
6064 return;
6065 }
6066 if (view instanceof ExpandableNotificationRow) {
6067 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Will Brockmane718d582019-01-17 16:38:38 -05006068 mMetricsLogger.write(row.getStatusBarNotification().getLogMaker()
6069 .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
6070 .setType(MetricsEvent.TYPE_ACTION)
6071 );
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006072 }
6073 mLongPressListener.onLongPress(view, x, y, item);
6074 }
6075
6076 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006077 public void onMenuReset(View row) {
6078 View translatingParentView = mSwipeHelper.getTranslatingParentView();
6079 if (translatingParentView != null && row == translatingParentView) {
6080 mSwipeHelper.clearExposedMenuView();
6081 mSwipeHelper.clearTranslatingParentView();
Gus Prevas211181532018-12-13 14:49:33 -05006082 if (row instanceof ExpandableNotificationRow) {
6083 mHeadsUpManager.setMenuShown(
6084 ((ExpandableNotificationRow) row).getEntry(), false);
6085
6086 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006087 }
6088 }
6089
6090 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006091 public void onMenuShown(View row) {
6092 if (row instanceof ExpandableNotificationRow) {
Gus Prevas211181532018-12-13 14:49:33 -05006093 ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
Will Brockmane718d582019-01-17 16:38:38 -05006094 mMetricsLogger.write(notificationRow.getStatusBarNotification().getLogMaker()
6095 .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
6096 .setType(MetricsEvent.TYPE_ACTION));
Gus Prevas211181532018-12-13 14:49:33 -05006097 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
Evan Lairde55c6012019-03-13 12:54:37 -04006098 mSwipeHelper.onMenuShown(row);
Steve Elliottff6c25e2019-05-30 17:38:52 -04006099 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
6100 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
6101 false /* resetMenu */);
Evan Lairde55c6012019-03-13 12:54:37 -04006102
6103 // Check to see if we want to go directly to the notfication guts
6104 NotificationMenuRowPlugin provider = notificationRow.getProvider();
6105 if (provider.shouldShowGutsOnSnapOpen()) {
6106 MenuItem item = provider.menuItemToExposeOnSnap();
6107 if (item != null) {
6108 Point origin = provider.getRevealAnimationOrigin();
Steve Elliottff6c25e2019-05-30 17:38:52 -04006109 mNotificationGutsManager.openGuts(row, origin.x, origin.y, item);
Evan Lairde55c6012019-03-13 12:54:37 -04006110 } else {
6111 Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
6112 + "menu item in menuItemtoExposeOnSnap. Skipping.");
6113 }
6114
6115 // Close the menu row since we went directly to the guts
6116 resetExposedMenuView(false, true);
6117 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006118 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006119 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006120 };
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006121
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006122 @ShadeViewRefactor(RefactorComponent.INPUT)
6123 private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
6124 new NotificationSwipeHelper.NotificationCallback() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006125 @Override
6126 public void onDismiss() {
6127 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
6128 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
6129 false /* resetMenu */);
6130 }
6131
6132 @Override
6133 public void onSnooze(StatusBarNotification sbn,
6134 NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
6135 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
6136 }
6137
6138 @Override
Selim Cinekae55d832019-02-22 17:43:43 -08006139 public boolean shouldDismissQuickly() {
6140 return NotificationStackScrollLayout.this.isExpanded() && mAmbientState.isFullyAwake();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006141 }
6142
6143 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006144 public void onDragCancelled(View v) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006145 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006146 mFalsingManager.onNotificatonStopDismissing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006147 }
6148
6149 /**
6150 * Handles cleanup after the given {@code view} has been fully swiped out (including
6151 * re-invoking dismiss logic in case the notification has not made its way out yet).
6152 */
6153 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006154 public void onChildDismissed(View view) {
6155 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6156 if (!row.isDismissed()) {
6157 handleChildViewDismissed(view);
6158 }
6159 ViewGroup transientContainer = row.getTransientContainer();
6160 if (transientContainer != null) {
6161 transientContainer.removeTransientView(view);
6162 }
6163 }
6164
6165 /**
6166 * Starts up notification dismiss and tells the notification, if any, to remove itself from
6167 * layout.
6168 *
6169 * @param view view (e.g. notification) to dismiss from the layout
6170 */
6171
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006172 public void handleChildViewDismissed(View view) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006173 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006174 if (mDismissAllInProgress) {
6175 return;
6176 }
6177
6178 boolean isBlockingHelperShown = false;
6179
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006180 mAmbientState.onDragFinished(view);
6181 updateContinuousShadowDrawing();
6182
6183 if (view instanceof ExpandableNotificationRow) {
6184 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6185 if (row.isHeadsUp()) {
6186 mHeadsUpManager.addSwipedOutNotification(
6187 row.getStatusBarNotification().getKey());
6188 }
6189 isBlockingHelperShown =
6190 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
6191 }
6192
6193 if (!isBlockingHelperShown) {
6194 mSwipedOutViews.add(view);
6195 }
6196 mFalsingManager.onNotificationDismissed();
6197 if (mFalsingManager.shouldEnforceBouncer()) {
6198 mStatusBar.executeRunnableDismissingKeyguard(
6199 null,
6200 null /* cancelAction */,
6201 false /* dismissShade */,
6202 true /* afterKeyguardGone */,
6203 false /* deferred */);
6204 }
6205 }
6206
6207 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006208 public boolean isAntiFalsingNeeded() {
6209 return onKeyguard();
6210 }
6211
6212 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006213 public View getChildAtPosition(MotionEvent ev) {
6214 View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
6215 ev.getY());
6216 if (child instanceof ExpandableNotificationRow) {
6217 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
6218 ExpandableNotificationRow parent = row.getNotificationParent();
6219 if (parent != null && parent.areChildrenExpanded()
6220 && (parent.areGutsExposed()
6221 || mSwipeHelper.getExposedMenuView() == parent
6222 || (parent.getNotificationChildren().size() == 1
Evan Laird94492852018-10-25 13:43:01 -04006223 && parent.getEntry().isClearable()))) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006224 // In this case the group is expanded and showing the menu for the
6225 // group, further interaction should apply to the group, not any
6226 // child notifications so we use the parent of the child. We also do the same
6227 // if we only have a single child.
6228 child = parent;
6229 }
6230 }
6231 return child;
6232 }
6233
6234 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006235 public void onBeginDrag(View v) {
6236 mFalsingManager.onNotificatonStartDismissing();
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006237 setSwipingInProgress(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006238 mAmbientState.onBeginDrag((ExpandableView) v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006239 updateContinuousShadowDrawing();
Selim Cinekae55d832019-02-22 17:43:43 -08006240 updateContinuousBackgroundDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006241 requestChildrenUpdate();
6242 }
6243
6244 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006245 public void onChildSnappedBack(View animView, float targetLeft) {
6246 mAmbientState.onDragFinished(animView);
6247 updateContinuousShadowDrawing();
Selim Cinekae55d832019-02-22 17:43:43 -08006248 updateContinuousBackgroundDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006249 }
6250
6251 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006252 public boolean updateSwipeProgress(View animView, boolean dismissable,
6253 float swipeProgress) {
6254 // Returning true prevents alpha fading.
6255 return !mFadeNotificationsOnDismiss;
6256 }
6257
6258 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006259 public float getFalsingThresholdFactor() {
6260 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
6261 }
6262
6263 @Override
Gus Prevasc4e68d42019-01-17 15:45:21 -05006264 public int getConstrainSwipeStartPosition() {
6265 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
6266 if (menuRow != null) {
6267 return Math.abs(menuRow.getMenuSnapTarget());
6268 }
6269 return 0;
6270 }
6271
6272 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006273 public boolean canChildBeDismissed(View v) {
Ned Burns61269442019-05-02 18:27:23 -04006274 return StackScrollAlgorithm.canChildBeDismissed(v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006275 }
Gus Prevas37d67e22018-11-02 14:48:55 -04006276
6277 @Override
6278 public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
Evan Laird30b9b162019-04-24 15:22:24 -04006279 //TODO: b/131242807 for why this doesn't do anything with direction
6280 return canChildBeDismissed(v);
Gus Prevas37d67e22018-11-02 14:48:55 -04006281 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006282 };
6283
6284 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
6285
6286 @ShadeViewRefactor(RefactorComponent.INPUT)
6287 private final DragDownCallback mDragDownCallback = new DragDownCallback() {
6288
6289 /* Only ever called as a consequence of a lockscreen expansion gesture. */
6290 @Override
6291 public boolean onDraggedDown(View startingChild, int dragLengthY) {
6292 if (mStatusBarState == StatusBarState.KEYGUARD
Lucas Dupin55c6e802018-09-27 18:07:36 -07006293 && hasActiveNotifications()) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006294 mLockscreenGestureLogger.write(
6295 MetricsEvent.ACTION_LS_SHADE,
6296 (int) (dragLengthY / mDisplayMetrics.density),
6297 0 /* velocityDp - N/A */);
6298
Selim Cinekc1d9ab22019-05-21 18:08:30 -07006299 if (!mAmbientState.isDozing() || startingChild != null) {
Lucas Dupin55c6e802018-09-27 18:07:36 -07006300 // We have notifications, go to locked shade.
Jason Monk297c04e2018-08-23 17:16:59 -04006301 mShadeController.goToLockedShade(startingChild);
Lucas Dupin55c6e802018-09-27 18:07:36 -07006302 if (startingChild instanceof ExpandableNotificationRow) {
6303 ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
6304 row.onExpandedByGesture(true /* drag down is always an open */);
6305 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006306 }
Lucas Dupin55c6e802018-09-27 18:07:36 -07006307
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006308 return true;
6309 } else {
6310 // abort gesture.
6311 return false;
6312 }
6313 }
6314
6315 @Override
6316 public void onDragDownReset() {
6317 setDimmed(true /* dimmed */, true /* animated */);
6318 resetScrollPosition();
6319 resetCheckSnoozeLeavebehind();
6320 }
6321
6322 @Override
6323 public void onCrossedThreshold(boolean above) {
6324 setDimmed(!above /* dimmed */, true /* animate */);
6325 }
6326
6327 @Override
6328 public void onTouchSlopExceeded() {
6329 cancelLongPress();
6330 checkSnoozeLeavebehind();
6331 }
6332
6333 @Override
6334 public void setEmptyDragAmount(float amount) {
6335 mNotificationPanel.setEmptyDragAmount(amount);
6336 }
6337
6338 @Override
6339 public boolean isFalsingCheckNeeded() {
6340 return mStatusBarState == StatusBarState.KEYGUARD;
6341 }
6342 };
6343
6344 public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
6345
6346 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6347 private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
6348 @Override
6349 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6350 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6351 }
6352
6353 @Override
6354 public boolean isExpanded() {
6355 return mIsExpanded;
6356 }
6357
6358 @Override
6359 public Context getContext() {
6360 return mContext;
6361 }
6362 };
6363
6364 public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
6365
6366
6367 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6368 private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
6369 @Override
6370 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
6371 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
6372 && (mIsExpanded || changedRow.isPinned());
6373 if (animated) {
6374 mExpandedGroupView = changedRow;
6375 mNeedsAnimation = true;
6376 }
6377 changedRow.setChildrenExpanded(expanded, animated);
6378 if (!mGroupExpandedForMeasure) {
6379 onHeightChanged(changedRow, false /* needsAnimation */);
6380 }
6381 runAfterAnimationFinished(new Runnable() {
6382 @Override
6383 public void run() {
6384 changedRow.onFinishedExpansionChange();
6385 }
6386 });
6387 }
6388
6389 @Override
6390 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
6391 mStatusBar.requestNotificationUpdate();
6392 }
6393
6394 @Override
6395 public void onGroupsChanged() {
6396 mStatusBar.requestNotificationUpdate();
6397 }
6398 };
6399
6400 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6401 private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
6402 @Override
6403 public ExpandableView getChildAtPosition(float touchX, float touchY) {
6404 return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY);
6405 }
6406
6407 @Override
6408 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6409 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6410 }
6411
6412 @Override
6413 public boolean canChildBeExpanded(View v) {
6414 return v instanceof ExpandableNotificationRow
6415 && ((ExpandableNotificationRow) v).isExpandable()
6416 && !((ExpandableNotificationRow) v).areGutsExposed()
6417 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
6418 }
6419
6420 /* Only ever called as a consequence of an expansion gesture in the shade. */
6421 @Override
6422 public void setUserExpandedChild(View v, boolean userExpanded) {
6423 if (v instanceof ExpandableNotificationRow) {
6424 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
6425 if (userExpanded && onKeyguard()) {
6426 // Due to a race when locking the screen while touching, a notification may be
6427 // expanded even after we went back to keyguard. An example of this happens if
6428 // you click in the empty space while expanding a group.
6429
6430 // We also need to un-user lock it here, since otherwise the content height
6431 // calculated might be wrong. We also can't invert the two calls since
6432 // un-userlocking it will trigger a layout switch in the content view.
6433 row.setUserLocked(false);
6434 updateContentHeight();
6435 notifyHeightChangeListener(row);
6436 return;
6437 }
6438 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
6439 row.onExpandedByGesture(userExpanded);
6440 }
6441 }
6442
6443 @Override
6444 public void setExpansionCancelled(View v) {
6445 if (v instanceof ExpandableNotificationRow) {
6446 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
6447 }
6448 }
6449
6450 @Override
6451 public void setUserLockedChild(View v, boolean userLocked) {
6452 if (v instanceof ExpandableNotificationRow) {
6453 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
6454 }
6455 cancelLongPress();
6456 requestDisallowInterceptTouchEvent(true);
6457 }
6458
6459 @Override
6460 public void expansionStateChanged(boolean isExpanding) {
6461 mExpandingNotification = isExpanding;
6462 if (!mExpandedInThisMotion) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05006463 if (ANCHOR_SCROLLING) {
6464 // TODO
6465 } else {
6466 mMaxScrollAfterExpand = mOwnScrollY;
6467 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006468 mExpandedInThisMotion = true;
6469 }
6470 }
6471
6472 @Override
6473 public int getMaxExpandHeight(ExpandableView view) {
6474 return view.getMaxContentHeight();
6475 }
6476 };
6477
6478 public ExpandHelper.Callback getExpandHelperCallback() {
6479 return mExpandHelperCallback;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006480 }
Ned Burns61269442019-05-02 18:27:23 -04006481
6482 /** Enum for selecting some or all notification rows (does not included non-notif views). */
6483 @Retention(SOURCE)
6484 @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
6485 public @interface SelectedRows {}
6486 /** All rows representing notifs. */
6487 public static final int ROWS_ALL = 0;
6488 /** Only rows where entry.isHighPriority() is true. */
6489 public static final int ROWS_HIGH_PRIORITY = 1;
6490 /** Only rows where entry.isHighPriority() is false. */
6491 public static final int ROWS_GENTLE = 2;
Selim Cinek67b22602014-03-10 15:40:16 +01006492}