blob: 5bd6cab1809beeb9975b45f6acbd648433244459 [file] [log] [blame]
Selim Cinek67b22602014-03-10 15:40:16 +01001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.stack;
Selim Cinek67b22602014-03-10 15:40:16 +010018
Gus Prevas59ec2ff2018-12-28 16:20:28 -050019import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
Gus Prevase83700cb2018-12-14 11:42:51 -050020import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
Gus Prevas0fa58d62019-01-11 13:58:40 -050021import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
Gus Prevase83700cb2018-12-14 11:42:51 -050022import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
Gus Prevas33619af2018-10-26 15:40:27 -040023import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
Gus Prevas59ec2ff2018-12-28 16:20:28 -050024import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
Selim Cinek2627d722018-01-19 12:16:49 -080025
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;
Jason Monke59dc402018-08-16 12:05:01 -040035import android.app.WallpaperManager;
Selim Cinek67b22602014-03-10 15:40:16 +010036import android.content.Context;
Jason Monke59dc402018-08-16 12:05:01 -040037import android.content.Intent;
Selim Cinek67b22602014-03-10 15:40:16 +010038import android.content.res.Configuration;
Anthony Chen3cb3ad92016-12-01 10:58:47 -080039import android.content.res.Resources;
Selim Cinek67b22602014-03-10 15:40:16 +010040import android.graphics.Canvas;
Lucas Dupind285cf02018-01-18 09:18:23 -080041import android.graphics.Color;
Lucas Dupin64e2f572019-03-21 14:21:14 -070042import android.graphics.Outline;
Selim Cinek67b22602014-03-10 15:40:16 +010043import android.graphics.Paint;
Evan Lairde55c6012019-03-13 12:54:37 -040044import android.graphics.Point;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +010045import android.graphics.PointF;
Selim Cinek6811d722016-01-19 17:53:12 -080046import android.graphics.PorterDuff;
47import android.graphics.PorterDuffXfermode;
48import android.graphics.Rect;
Selim Cinekc22fff62016-05-20 12:44:30 -070049import android.os.Bundle;
Jason Monke59dc402018-08-16 12:05:01 -040050import android.os.ServiceManager;
51import android.provider.Settings;
Mady Mellorc2ff0112019-03-28 14:18:06 -070052import android.service.notification.NotificationListenerService;
Mady Mellor95d743c2017-01-10 12:05:27 -080053import android.service.notification.StatusBarNotification;
Selim Cinek67b22602014-03-10 15:40:16 +010054import android.util.AttributeSet;
Jason Monke59dc402018-08-16 12:05:01 -040055import android.util.DisplayMetrics;
Selim Cinek67b22602014-03-10 15:40:16 +010056import android.util.Log;
Lucas Dupind285cf02018-01-18 09:18:23 -080057import android.util.MathUtils;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070058import android.util.Pair;
Lucas Dupine17ce522017-07-17 15:45:06 -070059import android.view.ContextThemeWrapper;
Selim Cinek11e33232016-08-05 15:30:53 -070060import android.view.InputDevice;
Jason Monke59dc402018-08-16 12:05:01 -040061import android.view.LayoutInflater;
Selim Cinek67b22602014-03-10 15:40:16 +010062import android.view.MotionEvent;
63import android.view.VelocityTracker;
64import android.view.View;
65import android.view.ViewConfiguration;
66import android.view.ViewGroup;
Lucas Dupin64e2f572019-03-21 14:21:14 -070067import android.view.ViewOutlineProvider;
Selim Cinek343e6e22014-04-11 21:23:30 +020068import android.view.ViewTreeObserver;
Adrian Roos5153d4a2016-03-22 10:01:56 -070069import android.view.WindowInsets;
Selim Cinekc22fff62016-05-20 12:44:30 -070070import android.view.accessibility.AccessibilityEvent;
71import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek572bbd42014-04-25 16:43:27 +020072import android.view.animation.AnimationUtils;
Selim Cinek614576e2016-01-20 10:54:09 -080073import android.view.animation.Interpolator;
Selim Cinek67b22602014-03-10 15:40:16 +010074import android.widget.OverScroller;
Selim Cinek41fe89a2016-06-02 15:27:56 -070075import android.widget.ScrollView;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070076
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040077import com.android.internal.annotations.VisibleForTesting;
78import com.android.internal.graphics.ColorUtils;
Mady Mellora41587b2016-02-11 18:43:06 -080079import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010080import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monke59dc402018-08-16 12:05:01 -040081import com.android.internal.statusbar.IStatusBarService;
Lucas Dupin4e023812018-04-02 21:19:23 -070082import com.android.keyguard.KeyguardSliceView;
Lucas Dupine17ce522017-07-17 15:45:06 -070083import com.android.settingslib.Utils;
Rohan Shah524cf7b2018-03-15 14:40:02 -070084import com.android.systemui.Dependency;
Jason Monke59dc402018-08-16 12:05:01 -040085import com.android.systemui.Dumpable;
Selim Cinek67b22602014-03-10 15:40:16 +010086import com.android.systemui.ExpandHelper;
Winsonc0d70582016-01-29 10:24:39 -080087import com.android.systemui.Interpolators;
Selim Cinek67b22602014-03-10 15:40:16 +010088import com.android.systemui.R;
89import com.android.systemui.SwipeHelper;
Dave Mankoffdde5ee62019-05-02 17:36:11 -040090import com.android.systemui.classifier.FalsingManagerFactory;
91import com.android.systemui.classifier.FalsingManagerFactory.FalsingManager;
Jason Monke59dc402018-08-16 12:05:01 -040092import com.android.systemui.colorextraction.SysuiColorExtractor;
Ned Burns9eb06332019-04-23 16:02:12 -040093import com.android.systemui.plugins.ActivityStarter;
Mady Mellor95d743c2017-01-10 12:05:27 -080094import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
95import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040096import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
Mady Mellor95d743c2017-01-10 12:05:27 -080097import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Beverly8fdb5332019-02-04 14:29:49 -050098import com.android.systemui.plugins.statusbar.StatusBarStateController;
99import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Selim Cinek3d6ae232019-01-04 14:14:33 -0800100import com.android.systemui.statusbar.AmbientPulseManager;
Jason Monke59dc402018-08-16 12:05:01 -0400101import com.android.systemui.statusbar.CommandQueue;
102import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
Jorim Jaggia2052ea2014-08-05 16:22:30 +0200103import com.android.systemui.statusbar.EmptyShadeView;
Jason Monk297c04e2018-08-23 17:16:59 -0400104import com.android.systemui.statusbar.NotificationLockscreenUserManager;
105import com.android.systemui.statusbar.NotificationRemoteInputManager;
106import com.android.systemui.statusbar.NotificationShelf;
107import com.android.systemui.statusbar.RemoteInputController;
108import com.android.systemui.statusbar.StatusBarState;
Beverly8fdb5332019-02-04 14:29:49 -0500109import com.android.systemui.statusbar.SysuiStatusBarStateController;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700110import com.android.systemui.statusbar.notification.DynamicPrivacyController;
Jason Monk297c04e2018-08-23 17:16:59 -0400111import com.android.systemui.statusbar.notification.FakeShadowView;
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500112import com.android.systemui.statusbar.notification.NotificationEntryListener;
Jason Monk297c04e2018-08-23 17:16:59 -0400113import com.android.systemui.statusbar.notification.NotificationEntryManager;
114import com.android.systemui.statusbar.notification.NotificationUtils;
115import com.android.systemui.statusbar.notification.ShadeViewRefactor;
116import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
Jason Monk297c04e2018-08-23 17:16:59 -0400117import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -0500118import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Jason Monk297c04e2018-08-23 17:16:59 -0400119import com.android.systemui.statusbar.notification.logging.NotificationLogger;
120import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
Rohan Shah20790b82018-07-02 17:21:04 -0700121import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
122import com.android.systemui.statusbar.notification.row.ExpandableView;
123import com.android.systemui.statusbar.notification.row.FooterView;
124import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
Rohan Shah20790b82018-07-02 17:21:04 -0700125import com.android.systemui.statusbar.notification.row.NotificationGuts;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400126import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
Rohan Shah20790b82018-07-02 17:21:04 -0700127import com.android.systemui.statusbar.notification.row.NotificationSnooze;
128import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800129import com.android.systemui.statusbar.phone.DozeParameters;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700130import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900131import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400132import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
Jason Monke59dc402018-08-16 12:05:01 -0400133import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
Selim Cinekb5605e52015-02-20 18:21:41 +0100134import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monke59dc402018-08-16 12:05:01 -0400135import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700136import com.android.systemui.statusbar.phone.NotificationIconAreaController;
Jason Monke59dc402018-08-16 12:05:01 -0400137import com.android.systemui.statusbar.phone.NotificationPanelView;
Selim Cinekaac93252015-04-14 20:04:12 -0700138import com.android.systemui.statusbar.phone.ScrimController;
Jason Monk297c04e2018-08-23 17:16:59 -0400139import com.android.systemui.statusbar.phone.ShadeController;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800140import com.android.systemui.statusbar.phone.StatusBar;
Jason Monke59dc402018-08-16 12:05:01 -0400141import com.android.systemui.statusbar.policy.ConfigurationController;
142import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900143import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100144import com.android.systemui.statusbar.policy.ScrollAdapter;
Gus Prevas33619af2018-10-26 15:40:27 -0400145import com.android.systemui.tuner.TunerService;
Selim Cinek67b22602014-03-10 15:40:16 +0100146
Selim Cinek707e2072017-06-30 18:32:40 +0200147import java.io.FileDescriptor;
148import java.io.PrintWriter;
Ned Burns61269442019-05-02 18:27:23 -0400149import java.lang.annotation.Retention;
Selim Cinek572bbd42014-04-25 16:43:27 +0200150import java.util.ArrayList;
Selim Cinek33223572016-02-19 19:32:22 -0800151import java.util.Collections;
152import java.util.Comparator;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200153import java.util.HashSet;
Selim Cinekef8c2252017-02-10 14:52:18 -0800154import java.util.List;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800155import java.util.function.BiConsumer;
Selim Cinek572bbd42014-04-25 16:43:27 +0200156
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500157import javax.inject.Inject;
158import javax.inject.Named;
159
Selim Cinek67b22602014-03-10 15:40:16 +0100160/**
161 * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
162 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400163public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter,
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700164 NotificationListContainer, ConfigurationListener, Dumpable,
165 DynamicPrivacyController.Listener {
Selim Cinek67b22602014-03-10 15:40:16 +0100166
Selim Cinekd35c2792016-01-21 13:20:57 -0800167 public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800168 private static final String TAG = "StackScroller";
Selim Cinek67b22602014-03-10 15:40:16 +0100169 private static final boolean DEBUG = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200170 private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
171 private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200172 private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
Selim Cinek67b22602014-03-10 15:40:16 +0100173 /**
174 * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
175 */
Lucas Dupind285cf02018-01-18 09:18:23 -0800176 private static final int INVALID_POINTER = -1;
Gus Prevase2d6f042018-10-17 15:25:30 -0400177 static final int NUM_SECTIONS = 2;
178 /**
179 * The distance in pixels between sections when the sections are directly adjacent (no visible
180 * gap is drawn between them). In this case we don't want to round their corners.
181 */
182 private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
Selim Cinek3d6ae232019-01-04 14:14:33 -0800183 private final AmbientPulseManager mAmbientPulseManager;
Selim Cinek67b22602014-03-10 15:40:16 +0100184
Selim Cinek1408eb52014-06-02 14:45:38 +0200185 private ExpandHelper mExpandHelper;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400186 private final NotificationSwipeHelper mSwipeHelper;
Selim Cinek67b22602014-03-10 15:40:16 +0100187 private int mCurrentStackHeight = Integer.MAX_VALUE;
Selim Cinekd35c2792016-01-21 13:20:57 -0800188 private final Paint mBackgroundPaint = new Paint();
Adrian Roosf0b4f962017-05-25 11:53:11 -0700189 private final boolean mShouldDrawNotificationBackground;
Gus Prevas33619af2018-10-26 15:40:27 -0400190 private boolean mLowPriorityBeforeSpeedBump;
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500191 private final boolean mAllowLongPress;
Gus Prevasa18dc572019-01-14 16:11:22 -0500192 private boolean mDismissRtl;
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +0100193
Selim Cinekbc243a92016-09-27 16:35:13 -0700194 private float mExpandedHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100195 private int mOwnScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -0500196 private View mScrollAnchorView;
197 private int mScrollAnchorViewY;
Selim Cinek67b22602014-03-10 15:40:16 +0100198 private int mMaxLayoutHeight;
199
200 private VelocityTracker mVelocityTracker;
201 private OverScroller mScroller;
Gus Prevascdc98342019-01-14 14:29:44 -0500202 /** Last Y position reported by {@link #mScroller}, used to calculate scroll delta. */
203 private int mLastScrollerY;
204 /**
205 * True if the max position was set to a known position on the last call to {@link #mScroller}.
206 */
207 private boolean mIsScrollerBoundSet;
Ricky Waicd35def2016-05-03 11:07:07 +0100208 private Runnable mFinishScrollingCallback;
Selim Cinek67b22602014-03-10 15:40:16 +0100209 private int mTouchSlop;
210 private int mMinimumVelocity;
211 private int mMaximumVelocity;
Selim Cinek67b22602014-03-10 15:40:16 +0100212 private int mOverflingDistance;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200213 private float mMaxOverScroll;
Selim Cinek67b22602014-03-10 15:40:16 +0100214 private boolean mIsBeingDragged;
215 private int mLastMotionY;
Selim Cinek1408eb52014-06-02 14:45:38 +0200216 private int mDownX;
Dong-wan Kimb9266662016-09-21 13:08:30 -0700217 private int mActivePointerId = INVALID_POINTER;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100218 private boolean mTouchIsClick;
219 private float mInitialTouchX;
220 private float mInitialTouchY;
Selim Cinek67b22602014-03-10 15:40:16 +0100221
Selim Cinek67b22602014-03-10 15:40:16 +0100222 private Paint mDebugPaint;
Selim Cinek67b22602014-03-10 15:40:16 +0100223 private int mContentHeight;
Lucas Dupin60661a62018-04-12 10:50:13 -0700224 private int mIntrinsicContentHeight;
Selim Cinek67b22602014-03-10 15:40:16 +0100225 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +0100226 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -0800227 private int mIncreasedPaddingBetweenElements;
shawnlin8e4e92c2018-04-12 18:47:24 +0800228 private int mMaxTopPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200229 private int mTopPadding;
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700230 private int mBottomMargin;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700231 private int mBottomInset = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +0800232 private float mQsExpansionFraction;
Selim Cinek67b22602014-03-10 15:40:16 +0100233
234 /**
235 * The algorithm which calculates the properties for our children
236 */
Muyuan Li87798022016-04-07 17:51:25 -0700237 protected final StackScrollAlgorithm mStackScrollAlgorithm;
Selim Cinek67b22602014-03-10 15:40:16 +0100238
Selim Cinek281c2022016-10-13 19:14:43 -0700239 private final AmbientState mAmbientState;
Selim Cinekb5605e52015-02-20 18:21:41 +0100240 private NotificationGroupManager mGroupManager;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500241 private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700242 private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500243 private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
244 private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +0200245 private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
Selim Cineka59ecc32015-04-07 10:51:49 -0700246 private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
247 private ArrayList<View> mSwipedOutViews = new ArrayList<>();
Selim Cinek572bbd42014-04-25 16:43:27 +0200248 private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200249 private boolean mAnimationsEnabled;
Selim Cinek159ffdb2014-06-04 22:24:18 +0200250 private boolean mChangePositionInProgress;
Selim Cinekef5127e2015-12-21 16:55:58 -0800251 private boolean mChildTransferInProgress;
Selim Cinek1685e632014-04-08 02:27:49 +0200252
Selim Cinek8d9ff9c2014-05-12 15:13:04 +0200253 /**
254 * The raw amount of the overScroll on the top, which is not rubber-banded.
255 */
256 private float mOverScrolledTopPixels;
257
258 /**
259 * The raw amount of the overScroll on the bottom, which is not rubber-banded.
260 */
261 private float mOverScrolledBottomPixels;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900262 private NotificationLogger.OnChildLocationsChangedListener mListener;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200263 private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200264 private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +0100265 private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
Jorim Jaggi0dd68812014-05-01 19:17:37 +0200266 private boolean mNeedsAnimation;
267 private boolean mTopPaddingNeedsAnimation;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200268 private boolean mDimmedNeedsAnimation;
Jorim Jaggiae441282014-08-01 02:45:18 +0200269 private boolean mHideSensitiveNeedsAnimation;
John Spurlockbf370992014-06-17 13:58:31 -0400270 private boolean mDarkNeedsAnimation;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100271 private int mDarkAnimationOriginIndex;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200272 private boolean mActivateNeedsAnimation;
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200273 private boolean mGoToFullShadeNeedsAnimation;
Selim Cinek572bbd42014-04-25 16:43:27 +0200274 private boolean mIsExpanded = true;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200275 private boolean mChildrenUpdateRequested;
Selim Cinekc27437b2014-05-14 10:23:33 +0200276 private boolean mIsExpansionChanging;
Jorim Jaggie4b840d2015-06-30 16:19:17 -0700277 private boolean mPanelTracking;
Selim Cinek1408eb52014-06-02 14:45:38 +0200278 private boolean mExpandingNotification;
279 private boolean mExpandedInThisMotion;
shawnlin8e4e92c2018-04-12 18:47:24 +0800280 private boolean mShouldShowShelfOnly;
Muyuan Li84b45612016-06-01 11:05:08 -0700281 protected boolean mScrollingEnabled;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400282 protected FooterView mFooterView;
Muyuan Lidd9ae752016-05-13 16:45:43 -0700283 protected EmptyShadeView mEmptyShadeView;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400284 private boolean mDismissAllInProgress;
Anthony Chen7acbb772017-04-07 16:45:25 -0700285 private boolean mFadeNotificationsOnDismiss;
Selim Cinek1408eb52014-06-02 14:45:38 +0200286
287 /**
288 * Was the scroller scrolled to the top when the down motion was observed?
289 */
290 private boolean mScrolledToTopOnFirstDown;
Selim Cinek1408eb52014-06-02 14:45:38 +0200291 /**
292 * The minimal amount of over scroll which is needed in order to switch to the quick settings
293 * when over scrolling on a expanded card.
294 */
295 private float mMinTopOverScrollToEscape;
296 private int mIntrinsicPadding;
Selim Cinekd2281152015-04-10 14:37:46 -0700297 private float mStackTranslation;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200298 private float mTopPaddingOverflow;
Selim Cinek1408eb52014-06-02 14:45:38 +0200299 private boolean mDontReportNextOverScroll;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700300 private boolean mDontClampNextScroll;
Selim Cineka5e211b2014-08-11 17:35:48 +0200301 private boolean mNeedViewResizeAnimation;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500302 private ExpandableView mExpandedGroupView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700303 private boolean mEverythingNeedsAnimation;
Selim Cineka59ecc32015-04-07 10:51:49 -0700304
Selim Cinek1408eb52014-06-02 14:45:38 +0200305 /**
306 * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
307 * This is needed to avoid scrolling too far after the notification was collapsed in the same
308 * motion.
309 */
310 private int mMaxScrollAfterExpand;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000311 private ExpandableNotificationRow.LongPressListener mLongPressListener;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700312 boolean mCheckForLeavebehind;
Selim Cinek1408eb52014-06-02 14:45:38 +0200313
314 /**
315 * Should in this touch motion only be scrolling allowed? It's true when the scroller was
316 * animating.
317 */
318 private boolean mOnlyScrollingInThisMotion;
Adrian Roosfa139752016-04-27 09:59:08 -0700319 private boolean mDisallowDismissInThisMotion;
Selim Cineka59ecc32015-04-07 10:51:49 -0700320 private boolean mDisallowScrollingInThisMotion;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700321 private long mGoToFullShadeDelay;
Selim Cinek1f553cf2014-05-02 12:01:36 +0200322 private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
Selim Cinek572bbd42014-04-25 16:43:27 +0200323 = new ViewTreeObserver.OnPreDrawListener() {
324 @Override
325 public boolean onPreDraw() {
Adrian Roos181385c2016-05-05 17:45:44 -0400326 updateForcedScroll();
Selim Cinek1f553cf2014-05-02 12:01:36 +0200327 updateChildren();
328 mChildrenUpdateRequested = false;
329 getViewTreeObserver().removeOnPreDrawListener(this);
Selim Cinek572bbd42014-04-25 16:43:27 +0200330 return true;
331 }
332 };
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500333 private StatusBar mStatusBar;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +0100334 private int[] mTempInt2 = new int[2];
Selim Cinekb5605e52015-02-20 18:21:41 +0100335 private boolean mGenerateChildOrderChangedEvent;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700336 private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
Selim Cinek9dd0d042018-05-14 18:12:42 -0700337 private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700338 private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
339 = new HashSet<>();
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900340 private HeadsUpManagerPhone mHeadsUpManager;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800341 private final NotificationRoundnessManager mRoundnessManager;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700342 private boolean mTrackingHeadsUp;
Selim Cinekaac93252015-04-14 20:04:12 -0700343 private ScrimController mScrimController;
Selim Cinekbbc580b2015-06-03 14:11:03 +0200344 private boolean mForceNoOverlappingRendering;
Selim Cineke0890e52015-06-17 11:17:08 -0700345 private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700346 private FalsingManager mFalsingManager;
Selim Cinek6811d722016-01-19 17:53:12 -0800347 private boolean mAnimationRunning;
Selim Cinekc383fd02016-10-21 15:31:26 -0700348 private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
Selim Cinek6811d722016-01-19 17:53:12 -0800349 = new ViewTreeObserver.OnPreDrawListener() {
350 @Override
351 public boolean onPreDraw() {
Selim Cinekc383fd02016-10-21 15:31:26 -0700352 onPreDrawDuringAnimation();
Selim Cinek6811d722016-01-19 17:53:12 -0800353 return true;
354 }
355 };
Gus Prevase2d6f042018-10-17 15:25:30 -0400356 private NotificationSection[] mSections = new NotificationSection[NUM_SECTIONS];
Selim Cinek614576e2016-01-20 10:54:09 -0800357 private boolean mAnimateNextBackgroundTop;
Gus Prevase2d6f042018-10-17 15:25:30 -0400358 private boolean mAnimateNextBackgroundBottom;
359 private boolean mAnimateNextSectionBoundsChange;
Selim Cinekd35c2792016-01-21 13:20:57 -0800360 private int mBgColor;
361 private float mDimAmount;
362 private ValueAnimator mDimAnimator;
Selim Cinek33223572016-02-19 19:32:22 -0800363 private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400364 private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
Selim Cinekd35c2792016-01-21 13:20:57 -0800365 @Override
366 public void onAnimationEnd(Animator animation) {
367 mDimAnimator = null;
368 }
369 };
370 private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
371 = new ValueAnimator.AnimatorUpdateListener() {
372
373 @Override
374 public void onAnimationUpdate(ValueAnimator animation) {
375 setDimAmount((Float) animation.getAnimatedValue());
376 }
377 };
Muyuan Li4fe4a402016-03-30 16:50:11 -0700378 protected ViewGroup mQsContainer;
Selim Cinek33223572016-02-19 19:32:22 -0800379 private boolean mContinuousShadowUpdate;
Selim Cinekae55d832019-02-22 17:43:43 -0800380 private boolean mContinuousBackgroundUpdate;
Selim Cinek33223572016-02-19 19:32:22 -0800381 private ViewTreeObserver.OnPreDrawListener mShadowUpdater
382 = new ViewTreeObserver.OnPreDrawListener() {
383
384 @Override
385 public boolean onPreDraw() {
386 updateViewShadows();
387 return true;
388 }
389 };
Selim Cinekae55d832019-02-22 17:43:43 -0800390 private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> {
391 updateBackground();
392 return true;
393 };
Selim Cinek33223572016-02-19 19:32:22 -0800394 private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
395 @Override
396 public int compare(ExpandableView view, ExpandableView otherView) {
397 float endY = view.getTranslationY() + view.getActualHeight();
398 float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
399 if (endY < otherEndY) {
400 return -1;
401 } else if (endY > otherEndY) {
402 return 1;
403 } else {
404 // The two notifications end at the same location
405 return 0;
406 }
407 }
408 };
Lucas Dupin64e2f572019-03-21 14:21:14 -0700409 private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
410 @Override
411 public void getOutline(View view, Outline outline) {
Lucas Dupin9f1e9482019-04-01 11:04:10 -0700412 if (mAmbientState.isDarkAtAll() || !mShowDarkShelf) {
Selim Cinek9ad240c2019-04-08 19:07:15 -0700413 float xProgress = mDarkXInterpolator.getInterpolation(
414 (1 - mLinearDarkAmount) * mBackgroundXFactor);
415 outline.setRoundRect(mBackgroundAnimationRect,
416 MathUtils.lerp(mCornerRadius / 2.0f, mCornerRadius,
417 xProgress));
Lucas Dupin64e2f572019-03-21 14:21:14 -0700418 } else {
419 ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
420 }
421 }
422 };
Selim Cinek25503252016-03-03 15:31:43 -0800423 private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900424 private boolean mPulsing;
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700425 private boolean mGroupExpandedForMeasure;
Selim Cinekc22fff62016-05-20 12:44:30 -0700426 private boolean mScrollable;
Adrian Roos181385c2016-05-05 17:45:44 -0400427 private View mForcedScroll;
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200428
429 /**
430 * @see #setDarkAmount(float, float)
431 */
432 private float mInterpolatedDarkAmount = 0f;
433
434 /**
435 * @see #setDarkAmount(float, float)
436 */
437 private float mLinearDarkAmount = 0f;
Lucas Dupin439bd442018-06-12 15:05:28 -0700438
439 /**
440 * How fast the background scales in the X direction as a factor of the Y expansion.
441 */
442 private float mBackgroundXFactor = 1f;
Selim Cinek972123d2016-05-03 14:25:58 -0700443
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -0500444 private boolean mSwipingInProgress;
445
Lucas Dupine17ce522017-07-17 15:45:06 -0700446 private boolean mUsingLightTheme;
Selim Cinekbc243a92016-09-27 16:35:13 -0700447 private boolean mQsExpanded;
Selim Cinekef406062016-09-29 17:33:13 -0700448 private boolean mForwardScrollable;
449 private boolean mBackwardScrollable;
Selim Cinek281c2022016-10-13 19:14:43 -0700450 private NotificationShelf mShelf;
Selim Cinekad7fac02016-10-18 17:09:15 -0700451 private int mMaxDisplayedNotifications = -1;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800452 private int mStatusBarHeight;
Selim Cinek51d21972017-07-19 17:39:20 -0700453 private int mMinInteractionHeight;
Selim Cinek48ff9b42016-11-09 19:31:51 -0800454 private boolean mNoAmbient;
455 private final Rect mClipRect = new Rect();
456 private boolean mIsClipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700457 private Rect mRequestedClipBounds;
458 private boolean mInHeadsUpPinnedMode;
459 private boolean mHeadsUpAnimatingAway;
Selim Cinek355652a2016-12-07 13:32:12 -0800460 private int mStatusBarState;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100461 private int mCachedBackgroundColor;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700462 private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
Gus Prevascdc98342019-01-14 14:29:44 -0500463 private Runnable mReflingAndAnimateScroll = () -> {
464 if (ANCHOR_SCROLLING) {
465 maybeReflingScroller();
466 }
467 animateScroll();
468 };
Selim Cinek0fe07392017-11-09 13:26:34 -0800469 private int mCornerRadius;
Selim Cinek515b2032017-11-15 10:20:19 -0800470 private int mSidePaddings;
Lucas Dupin16cfe452018-02-08 13:14:50 -0800471 private final Rect mBackgroundAnimationRect = new Rect();
Lucas Dupin0cd882f2018-01-30 12:19:49 -0800472 private int mAntiBurnInOffsetX;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800473 private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
474 private int mHeadsUpInset;
Selim Cinekf0c79e12018-05-14 17:17:31 -0700475 private HeadsUpAppearanceController mHeadsUpAppearanceController;
Selim Cinek9bfc7a52018-06-11 16:09:00 -0700476 private NotificationIconAreaController mIconAreaController;
Lucas Dupinb46d0a22019-01-11 16:57:16 -0800477 private float mHorizontalPanelTranslation;
Jason Monk1fd3fc32018-08-14 17:20:09 -0400478 private final NotificationLockscreenUserManager mLockscreenUserManager =
479 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monk297c04e2018-08-23 17:16:59 -0400480 protected final NotificationGutsManager mGutsManager =
481 Dependency.get(NotificationGutsManager.class);
Jason Monke59dc402018-08-16 12:05:01 -0400482 private final Rect mTmpRect = new Rect();
483 private final NotificationEntryManager mEntryManager =
484 Dependency.get(NotificationEntryManager.class);
485 private final IStatusBarService mBarService = IStatusBarService.Stub.asInterface(
486 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Will Brockmane718d582019-01-17 16:38:38 -0500487 @VisibleForTesting
488 protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Jason Monke59dc402018-08-16 12:05:01 -0400489 private final NotificationRemoteInputManager mRemoteInputManager =
490 Dependency.get(NotificationRemoteInputManager.class);
491 private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
492
493 private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
494 private final LockscreenGestureLogger mLockscreenGestureLogger =
495 Dependency.get(LockscreenGestureLogger.class);
496 private final VisualStabilityManager mVisualStabilityManager =
497 Dependency.get(VisualStabilityManager.class);
498 protected boolean mClearAllEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +0100499
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200500 private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
Jason Monke59dc402018-08-16 12:05:01 -0400501 private NotificationPanelView mNotificationPanel;
Jason Monk297c04e2018-08-23 17:16:59 -0400502 private final ShadeController mShadeController = Dependency.get(ShadeController.class);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400503
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400504 private final NotificationGutsManager
505 mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
Ned Burns9eb06332019-04-23 16:02:12 -0400506 private final NotificationSectionsManager mSectionsManager;
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800507 /**
508 * If the {@link NotificationShelf} should be visible when dark.
509 */
510 private boolean mShowDarkShelf;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700511 private boolean mAnimateBottomOnLayout;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400512
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500513 @Inject
514 public NotificationStackScrollLayout(
515 @Named(VIEW_CONTEXT) Context context,
516 AttributeSet attrs,
Lucas Dupin00be88f2019-01-03 17:50:52 -0800517 @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
Selim Cinek3d6ae232019-01-04 14:14:33 -0800518 NotificationRoundnessManager notificationRoundnessManager,
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700519 AmbientPulseManager ambientPulseManager,
Ned Burns9eb06332019-04-23 16:02:12 -0400520 DynamicPrivacyController dynamicPrivacyController,
521 ActivityStarter activityStarter) {
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500522 super(context, attrs, 0, 0);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800523 Resources res = getResources();
524
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500525 mAllowLongPress = allowLongPress;
526
Gus Prevase2d6f042018-10-17 15:25:30 -0400527 for (int i = 0; i < NUM_SECTIONS; i++) {
528 mSections[i] = new NotificationSection(this);
529 }
530
Selim Cinek3d6ae232019-01-04 14:14:33 -0800531 mAmbientPulseManager = ambientPulseManager;
Ned Burns61269442019-05-02 18:27:23 -0400532
Ned Burns9eb06332019-04-23 16:02:12 -0400533 mSectionsManager =
534 new NotificationSectionsManager(
535 this,
536 activityStarter,
537 NotificationUtils.useNewInterruptionModel(context));
538 mSectionsManager.inflateViews(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
Ned Burns9eb06332019-04-23 16:02:12 -0400545 mAmbientState = new AmbientState(context, mSectionsManager);
Lucas Dupin00be88f2019-01-03 17:50:52 -0800546 mRoundnessManager = notificationRoundnessManager;
Selim Cinekd35c2792016-01-21 13:20:57 -0800547 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800548 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
549 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400550 mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
Selim Cinek1cf41c12014-08-12 20:06:19 +0200551 minHeight, maxHeight);
552 mExpandHelper.setEventSource(this);
553 mExpandHelper.setScrollAdapter(this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400554 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
555 getContext(), mMenuEventListener);
Muyuan Li333a4fc2016-04-16 17:13:46 -0700556 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100557 initView(context);
Dave Mankoffdde5ee62019-05-02 17:36:11 -0400558 mFalsingManager = FalsingManagerFactory.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800559 mShouldDrawNotificationBackground =
560 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700561 mFadeNotificationsOnDismiss =
562 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Selim Cinek29aab962018-02-27 17:05:45 -0800563 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
564 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800565 addOnExpandedHeightListener(mRoundnessManager::setExpanded);
Lucas Dupin64e2f572019-03-21 14:21:14 -0700566 setOutlineProvider(mOutlineProvider);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800567
Rohan Shah524cf7b2018-03-15 14:40:02 -0700568 // Blocking helper manager wants to know the expanded state, update as well.
569 NotificationBlockingHelperManager blockingHelperManager =
570 Dependency.get(NotificationBlockingHelperManager.class);
571 addOnExpandedHeightListener((height, unused) -> {
572 blockingHelperManager.setNotificationShadeExpanded(height);
573 });
574
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800575 updateWillNotDraw();
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) -> {
588 if (key.equals(LOW_PRIORITY)) {
589 mLowPriorityBeforeSpeedBump = "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 }
Gus Prevasa18dc572019-01-14 16:11:22 -0500593 }, LOW_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) {
627 setLongPressListener(mGutsManager::openGuts);
628 }
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 */
634 public float getPulseHeight() {
Selim Cinek3d6ae232019-01-04 14:14:33 -0800635 ActivatableNotificationView firstChild = getFirstChildWithBackground();
636 if (firstChild != null) {
637 return firstChild.getCollapsedHeight();
638 }
Selim Cinek34518f62019-02-28 19:41:18 -0800639 return 0f;
Selim Cinek3d6ae232019-01-04 14:14:33 -0800640 }
641
Jason Monke59dc402018-08-16 12:05:01 -0400642 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400643 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400644 public void onDensityOrFontScaleChanged() {
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800645 reinflateViews();
646 }
647
648 private void reinflateViews() {
Jason Monke59dc402018-08-16 12:05:01 -0400649 inflateFooterView();
650 inflateEmptyShadeView();
651 updateFooter();
Ned Burns9eb06332019-04-23 16:02:12 -0400652 mSectionsManager.inflateViews(mContext);
Jason Monke59dc402018-08-16 12:05:01 -0400653 }
654
655 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400656 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400657 public void onThemeChanged() {
658 int which;
659 if (mStatusBarState == StatusBarState.KEYGUARD
660 || mStatusBarState == StatusBarState.SHADE_LOCKED) {
661 which = WallpaperManager.FLAG_LOCK;
662 } else {
663 which = WallpaperManager.FLAG_SYSTEM;
664 }
665 final boolean useDarkText = mColorExtractor.getColors(which,
666 true /* ignoreVisibility */).supportsDarkText();
667 updateDecorViews(useDarkText);
668
669 updateFooter();
670 }
671
Fabian Kozynskid254b192019-02-05 13:42:58 -0500672 @Override
673 public void onOverlayChanged() {
674 int newRadius = mContext.getResources().getDimensionPixelSize(
675 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
676 if (mCornerRadius != newRadius) {
677 mCornerRadius = newRadius;
678 invalidate();
679 }
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800680 reinflateViews();
Fabian Kozynskid254b192019-02-05 13:42:58 -0500681 }
682
Jason Monke59dc402018-08-16 12:05:01 -0400683 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400684 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400685 public void updateFooter() {
Ned Burns61269442019-05-02 18:27:23 -0400686 boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL);
Jason Monke59dc402018-08-16 12:05:01 -0400687 boolean showFooterView = (showDismissView ||
688 mEntryManager.getNotificationData().getActiveNotifications().size() != 0)
689 && mStatusBarState != StatusBarState.KEYGUARD
690 && !mRemoteInputManager.getController().isRemoteInputActive();
691
692 updateFooterView(showFooterView, showDismissView);
693 }
694
695 /**
696 * Return whether there are any clearable notifications
697 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400698 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -0400699 public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
Jason Monke59dc402018-08-16 12:05:01 -0400700 int childCount = getChildCount();
701 for (int i = 0; i < childCount; i++) {
702 View child = getChildAt(i);
703 if (!(child instanceof ExpandableNotificationRow)) {
704 continue;
705 }
Ned Burns61269442019-05-02 18:27:23 -0400706 final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
707 if (row.canViewBeDismissed() && matchesSelection(row, selection)) {
Jason Monke59dc402018-08-16 12:05:01 -0400708 return true;
709 }
710 }
711 return false;
712 }
713
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400714 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400715 public RemoteInputController.Delegate createDelegate() {
Jason Monke59dc402018-08-16 12:05:01 -0400716 return new RemoteInputController.Delegate() {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500717 public void setRemoteInputActive(NotificationEntry entry,
Jason Monke59dc402018-08-16 12:05:01 -0400718 boolean remoteInputActive) {
719 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
Evan Laird94492852018-10-25 13:43:01 -0400720 entry.notifyHeightChanged(true /* needsAnimation */);
Jason Monke59dc402018-08-16 12:05:01 -0400721 updateFooter();
722 }
723
Ned Burnsf81c4c42019-01-07 14:10:43 -0500724 public void lockScrollTo(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400725 NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
Jason Monke59dc402018-08-16 12:05:01 -0400726 }
727
728 public void requestDisallowLongPressAndDismiss() {
729 requestDisallowLongPress();
730 requestDisallowDismiss();
731 }
732 };
Selim Cinek67b22602014-03-10 15:40:16 +0100733 }
734
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900735 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400736 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400737 protected void onAttachedToWindow() {
738 super.onAttachedToWindow();
Beverly8fdb5332019-02-04 14:29:49 -0500739 ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class))
740 .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
Jason Monke59dc402018-08-16 12:05:01 -0400741 Dependency.get(ConfigurationController.class).addCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400742 }
743
744 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400745 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400746 protected void onDetachedFromWindow() {
747 super.onDetachedFromWindow();
Jason Monkaf08c152018-12-04 11:12:39 -0500748 Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Jason Monke59dc402018-08-16 12:05:01 -0400749 Dependency.get(ConfigurationController.class).removeCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400750 }
751
752 @Override
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400753 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -0800754 public NotificationSwipeActionHelper getSwipeActionHelper() {
755 return mSwipeHelper;
756 }
757
Selim Cinek67b22602014-03-10 15:40:16 +0100758 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400759 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupinf03e7522018-06-25 16:21:13 -0700760 public void onUiModeChanged() {
761 mBgColor = mContext.getColor(R.color.notification_shade_background_color);
762 updateBackgroundDimming();
Selim Cinekab9c7b22018-12-11 18:15:47 -0800763 mShelf.onUiModeChanged();
Ned Burns9eb06332019-04-23 16:02:12 -0400764 mSectionsManager.onUiModeChanged();
Lucas Dupinf03e7522018-06-25 16:21:13 -0700765 }
766
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400767 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Selim Cinek67b22602014-03-10 15:40:16 +0100768 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800769 if (mShouldDrawNotificationBackground
Gus Prevase2d6f042018-10-17 15:25:30 -0400770 && (mSections[0].getCurrentBounds().top
771 < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
772 || mAmbientState.isDark())) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800773 drawBackground(canvas);
Gus Prevas211181532018-12-13 14:49:33 -0500774 } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
775 drawHeadsUpBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700776 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800777
Selim Cinek67b22602014-03-10 15:40:16 +0100778 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800779 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100780 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800781 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100782 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200783 y = getHeight() - getEmptyBottomMargin();
784 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100785 }
786 }
787
Gus Prevas0fa58d62019-01-11 13:58:40 -0500788 @Override
789 public void draw(Canvas canvas) {
790 super.draw(canvas);
791
792 if (DEBUG && ANCHOR_SCROLLING) {
793 if (mScrollAnchorView instanceof ExpandableNotificationRow) {
794 canvas.drawRect(0,
795 mScrollAnchorView.getTranslationY(),
796 getWidth(),
797 mScrollAnchorView.getTranslationY()
798 + ((ExpandableNotificationRow) mScrollAnchorView).getActualHeight(),
799 mDebugPaint);
800 canvas.drawText(Integer.toString(mScrollAnchorViewY), getWidth() - 200,
801 mScrollAnchorView.getTranslationY() + 30, mDebugPaint);
802 int y = (int) mShelf.getTranslationY();
803 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
804 }
Gus Prevascdc98342019-01-14 14:29:44 -0500805 canvas.drawText(Integer.toString(getMaxNegativeScrollAmount()), getWidth() - 100,
806 getIntrinsicPadding() + 30, mDebugPaint);
807 canvas.drawText(Integer.toString(getMaxPositiveScrollAmount()), getWidth() - 100,
808 getHeight() - 30, mDebugPaint);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500809 }
810 }
811
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400812 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupind285cf02018-01-18 09:18:23 -0800813 private void drawBackground(Canvas canvas) {
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700814 int lockScreenLeft = mSidePaddings;
815 int lockScreenRight = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -0400816 int lockScreenTop = mSections[0].getCurrentBounds().top;
817 int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom;
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700818 int darkLeft = getWidth() / 2;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800819 int darkTop = mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800820
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700821 float yProgress = 1 - mInterpolatedDarkAmount;
822 float xProgress = mDarkXInterpolator.getInterpolation(
823 (1 - mLinearDarkAmount) * mBackgroundXFactor);
Lucas Dupin60661a62018-04-12 10:50:13 -0700824
Gus Prevase2d6f042018-10-17 15:25:30 -0400825 int left = (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress);
826 int right = (int) MathUtils.lerp(darkLeft, lockScreenRight, xProgress);
827 int top = (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress);
828 int bottom = (int) MathUtils.lerp(darkTop, lockScreenBottom, yProgress);
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700829 mBackgroundAnimationRect.set(
Gus Prevase2d6f042018-10-17 15:25:30 -0400830 left,
831 top,
832 right,
833 bottom);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200834
Gus Prevase2d6f042018-10-17 15:25:30 -0400835 int backgroundTopAnimationOffset = top - lockScreenTop;
836 // TODO(kprevas): this may not be necessary any more since we don't display the shelf in AOD
837 boolean anySectionHasVisibleChild = false;
838 for (NotificationSection section : mSections) {
839 if (section.getFirstVisibleChild() != null) {
840 anySectionHasVisibleChild = true;
841 break;
842 }
843 }
844 if (!mAmbientState.isDark() || anySectionHasVisibleChild) {
845 drawBackgroundRects(canvas, left, right, top, backgroundTopAnimationOffset);
Lucas Dupind285cf02018-01-18 09:18:23 -0800846 }
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700847
Lucas Dupin16cfe452018-02-08 13:14:50 -0800848 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800849 }
850
Gus Prevase2d6f042018-10-17 15:25:30 -0400851 /**
852 * Draws round rects for each background section.
853 *
854 * We want to draw a round rect for each background section as defined by {@link #mSections}.
855 * However, if two sections are directly adjacent with no gap between them (e.g. on the
856 * lockscreen where the shelf can appear directly below the high priority section, or while
857 * scrolling the shade so that the top of the shelf is right at the bottom of the high priority
858 * section), we don't want to round the adjacent corners.
859 *
860 * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we
861 * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect.
862 * This method tracks the top of each rect we need to draw, then iterates through the visible
863 * sections. If a section is not adjacent to the previous section, we draw the previous rect
864 * behind the sections we've accumulated up to that point, then start a new rect at the top of
865 * the current section. When we're done iterating we will always have one rect left to draw.
866 */
867 private void drawBackgroundRects(Canvas canvas, int left, int right, int top,
868 int animationYOffset) {
869 int backgroundRectTop = top;
870 int lastSectionBottom =
871 mSections[0].getCurrentBounds().bottom + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800872 int previousLeft = left;
873 boolean first = true;
Gus Prevase2d6f042018-10-17 15:25:30 -0400874 for (NotificationSection section : mSections) {
875 if (section.getFirstVisibleChild() == null) {
876 continue;
877 }
878 int sectionTop = section.getCurrentBounds().top + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800879 int ownLeft = Math.min(Math.max(left, section.getCurrentBounds().left), right);
Gus Prevase2d6f042018-10-17 15:25:30 -0400880 // If sections are directly adjacent to each other, we don't want to draw them
881 // as separate roundrects, as the rounded corners right next to each other look
882 // bad.
Selim Cinekae55d832019-02-22 17:43:43 -0800883 if (sectionTop - lastSectionBottom > DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX
884 || (previousLeft != ownLeft && !first)) {
885 canvas.drawRoundRect(ownLeft,
Gus Prevase2d6f042018-10-17 15:25:30 -0400886 backgroundRectTop,
887 right,
888 lastSectionBottom,
889 mCornerRadius, mCornerRadius, mBackgroundPaint);
890 backgroundRectTop = sectionTop;
891 }
Selim Cinekae55d832019-02-22 17:43:43 -0800892 previousLeft = ownLeft;
Gus Prevase2d6f042018-10-17 15:25:30 -0400893 lastSectionBottom =
894 section.getCurrentBounds().bottom + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800895 first = false;
Gus Prevase2d6f042018-10-17 15:25:30 -0400896 }
Selim Cinekae55d832019-02-22 17:43:43 -0800897 canvas.drawRoundRect(previousLeft,
Gus Prevase2d6f042018-10-17 15:25:30 -0400898 backgroundRectTop,
899 right,
900 lastSectionBottom,
901 mCornerRadius, mCornerRadius, mBackgroundPaint);
902 }
903
Gus Prevas211181532018-12-13 14:49:33 -0500904 private void drawHeadsUpBackground(Canvas canvas) {
905 int left = mSidePaddings;
906 int right = getWidth() - mSidePaddings;
907
908 float top = getHeight();
909 float bottom = 0;
910 int childCount = getChildCount();
911 for (int i = 0; i < childCount; i++) {
912 View child = getChildAt(i);
913 if (child.getVisibility() != View.GONE
914 && child instanceof ExpandableNotificationRow) {
915 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
916 if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0) {
917 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 float alpha =
938 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200939 alpha *= 1f - mInterpolatedDarkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -0700940 // We need to manually blend in the background color.
941 int scrimColor = mScrimController.getBackgroundColor();
942 int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
943
944 // Interpolate between semi-transparent notification panel background color
945 // and white AOD separator.
Lucas Dupinf03e7522018-06-25 16:21:13 -0700946 float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */,
947 mLinearDarkAmount);
Lucas Dupinb561eda2018-04-09 17:25:04 -0700948 int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800949
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100950 if (mCachedBackgroundColor != color) {
951 mCachedBackgroundColor = color;
952 mBackgroundPaint.setColor(color);
953 invalidate();
954 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800955 }
956
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400957 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +0100958 private void initView(Context context) {
959 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100960 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200961 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100962 final ViewConfiguration configuration = ViewConfiguration.get(context);
963 mTouchSlop = configuration.getScaledTouchSlop();
964 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
965 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100966 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700967
968 Resources res = context.getResources();
969 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800970 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700971 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700972 mPaddingBetweenElements = Math.max(1,
973 res.getDimensionPixelSize(R.dimen.notification_divider_height));
974 mIncreasedPaddingBetweenElements =
975 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
976 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200977 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800978 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700979 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800980 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700981 mMinInteractionHeight = res.getDimensionPixelSize(
982 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800983 mCornerRadius = res.getDimensionPixelSize(
984 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800985 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
986 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200987 }
988
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400989 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekaef92ef2014-06-06 18:06:04 +0200990 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700991 notifyHeightChangeListener(view, false /* needsAnimation */);
992 }
993
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400994 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -0700995 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +0200996 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700997 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +0200998 }
Selim Cinek67b22602014-03-10 15:40:16 +0100999 }
1000
1001 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001002 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001003 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1004 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -08001005
1006 int width = MeasureSpec.getSize(widthMeasureSpec);
1007 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
1008 MeasureSpec.getMode(widthMeasureSpec));
Evan Lairdeb7dbd52018-06-28 13:17:25 -04001009 // Don't constrain the height of the children so we know how big they'd like to be
1010 int childHeightSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
1011 MeasureSpec.UNSPECIFIED);
1012
Selim Cinekfa760d42016-05-10 15:50:53 -04001013 // We need to measure all children even the GONE ones, such that the heights are calculated
1014 // correctly as they are used to calculate how many we can fit on the screen.
1015 final int size = getChildCount();
1016 for (int i = 0; i < size; i++) {
Evan Lairdeb7dbd52018-06-28 13:17:25 -04001017 measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -04001018 }
Selim Cinek67b22602014-03-10 15:40:16 +01001019 }
1020
1021 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001022 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001023 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +01001024 // we layout all our children centered on the top
1025 float centerX = getWidth() / 2.0f;
1026 for (int i = 0; i < getChildCount(); i++) {
1027 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -04001028 // We need to layout all children even the GONE ones, such that the heights are
1029 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +01001030 float width = child.getMeasuredWidth();
1031 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +01001032 child.layout((int) (centerX - width / 2.0f),
1033 0,
1034 (int) (centerX + width / 2.0f),
1035 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +01001036 }
Jorim Jaggi1d480692014-05-20 19:41:58 +02001037 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +01001038 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02001039 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +02001040 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -08001041 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -07001042 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +01001043 }
1044
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001045 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5bc852a2015-12-21 12:19:09 -08001046 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
1047 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +02001048 mNeedViewResizeAnimation = true;
1049 mNeedsAnimation = true;
1050 }
Selim Cineka5e211b2014-08-11 17:35:48 +02001051 }
1052
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001053 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekdb167372016-11-17 15:41:17 -08001054 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
1055 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001056 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +02001057 }
1058
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001059 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001060 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001061 public void setChildLocationsChangedListener(
1062 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001063 mListener = listener;
1064 }
1065
Selim Cineka7d4f822016-12-06 14:34:47 -08001066 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001067 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001068 public boolean isInVisibleLocation(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001069 ExpandableNotificationRow row = entry.getRow();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001070 ExpandableViewState childViewState = row.getViewState();
1071
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001072 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001073 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001074 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +01001075 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001076 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +01001077 }
Selim Cineka7d4f822016-12-06 14:34:47 -08001078 if (row.getVisibility() != View.VISIBLE) {
1079 return false;
1080 }
1081 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001082 }
1083
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001084 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinek67b22602014-03-10 15:40:16 +01001085 private void setMaxLayoutHeight(int maxLayoutHeight) {
1086 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -07001087 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001088 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +01001089 }
1090
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001091 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001092 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001093 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -07001094 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001095 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +01001096 }
1097
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001098 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinekbc243a92016-09-27 16:35:13 -07001099 private void updateAlgorithmLayoutMinHeight() {
shawnlinc3457912018-05-15 16:39:56 +08001100 mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001101 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -07001102 }
1103
Selim Cinek67b22602014-03-10 15:40:16 +01001104 /**
1105 * Updates the children views according to the stack scroll algorithm. Call this whenever
1106 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
1107 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001108 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001109 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -08001110 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -08001111 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
1112 ? 0
1113 : mScroller.getCurrVelocity());
Gus Prevas0fa58d62019-01-11 13:58:40 -05001114 if (ANCHOR_SCROLLING) {
1115 mAmbientState.setAnchorViewIndex(indexOfChild(mScrollAnchorView));
1116 mAmbientState.setAnchorViewY(mScrollAnchorViewY);
1117 } else {
1118 mAmbientState.setScrollY(mOwnScrollY);
1119 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001120 mStackScrollAlgorithm.resetViewStates(mAmbientState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001121 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02001122 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +01001123 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +02001124 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +01001125 }
1126 }
1127
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001128 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc383fd02016-10-21 15:31:26 -07001129 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -07001130 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001131 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -07001132 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
1133 updateBackground();
1134 }
Selim Cinekc383fd02016-10-21 15:31:26 -07001135 }
1136
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001137 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001138 private void updateClippingToTopRoundedCorner() {
Arthur Hungc0ef5652018-05-22 14:00:42 +08001139 Float clipStart = (float) mTopPadding
Jason Monke59dc402018-08-16 12:05:01 -04001140 + mStackTranslation
1141 + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001142 Float clipEnd = clipStart + mCornerRadius;
1143 boolean first = true;
1144 for (int i = 0; i < getChildCount(); i++) {
1145 ExpandableView child = (ExpandableView) getChildAt(i);
1146 if (child.getVisibility() == GONE) {
1147 continue;
1148 }
1149 float start = child.getTranslationY();
Arthur Hungc0ef5652018-05-22 14:00:42 +08001150 float end = start + child.getActualHeight();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001151 boolean clip = clipStart > start && clipStart < end
1152 || clipEnd >= start && clipEnd <= end;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001153 clip &= !(first && isScrolledToTop());
Selim Cinekeccf4942018-05-30 09:55:36 -07001154 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
1155 : ExpandableView.NO_ROUNDNESS);
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001156 first = false;
1157 }
1158 }
1159
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001160 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek3776fe02016-02-04 13:32:43 -08001161 private void updateScrollStateForAddedChildren() {
1162 if (mChildrenToAddAnimated.isEmpty()) {
1163 return;
1164 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001165 if (!ANCHOR_SCROLLING) {
1166 for (int i = 0; i < getChildCount(); i++) {
1167 ExpandableView child = (ExpandableView) getChildAt(i);
1168 if (mChildrenToAddAnimated.contains(child)) {
1169 int startingPosition = getPositionInLinearLayout(child);
1170 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
1171 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
1172 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
1173 int childHeight = getIntrinsicHeight(child) + padding;
1174 if (startingPosition < mOwnScrollY) {
1175 // This child starts off screen, so let's keep it offscreen to keep the
1176 // others visible
Selim Cinek3776fe02016-02-04 13:32:43 -08001177
Gus Prevas0fa58d62019-01-11 13:58:40 -05001178 setOwnScrollY(mOwnScrollY + childHeight);
1179 }
Selim Cinek3776fe02016-02-04 13:32:43 -08001180 }
1181 }
1182 }
1183 clampScrollPosition();
1184 }
1185
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001186 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001187 private void updateForcedScroll() {
1188 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
1189 || !mForcedScroll.isAttachedToWindow())) {
1190 mForcedScroll = null;
1191 }
1192 if (mForcedScroll != null) {
1193 ExpandableView expandableView = (ExpandableView) mForcedScroll;
1194 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
1195 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -07001196 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -04001197
Gus Prevas0fa58d62019-01-11 13:58:40 -05001198 if (ANCHOR_SCROLLING) {
1199 // TODO
1200 } else {
1201 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -07001202
Gus Prevas0fa58d62019-01-11 13:58:40 -05001203 // Only apply the scroll if we're scrolling the view upwards, or the view is so
1204 // far up that it is not visible anymore.
1205 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1206 setOwnScrollY(targetScroll);
1207 }
Adrian Roos181385c2016-05-05 17:45:44 -04001208 }
1209 }
1210 }
1211
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001212 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek319bdc42014-05-01 23:01:58 +02001213 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +02001214 if (!mChildrenUpdateRequested) {
1215 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
1216 mChildrenUpdateRequested = true;
1217 invalidate();
1218 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001219 }
1220
Robert Snoeberger168949a2019-04-18 09:39:42 -04001221 /**
1222 * Returns best effort count of visible notifications.
1223 */
1224 public int getVisibleNotificationCount() {
1225 int count = 0;
1226 for (int i = 0; i < getChildCount(); i++) {
1227 final View child = getChildAt(i);
1228 if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
1229 count++;
1230 }
1231 }
1232 return count;
1233 }
1234
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001235 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001236 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +02001237 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +01001238 }
1239
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001240 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekf7a14c02014-07-07 14:01:46 +02001241 private void clampScrollPosition() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001242 if (ANCHOR_SCROLLING) {
1243 // TODO
1244 } else {
1245 int scrollRange = getScrollRange();
1246 if (scrollRange < mOwnScrollY) {
1247 setOwnScrollY(scrollRange);
1248 }
Selim Cinek67b22602014-03-10 15:40:16 +01001249 }
1250 }
1251
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001252 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001253 public int getTopPadding() {
1254 return mTopPadding;
1255 }
1256
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001257 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001258 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupin00be88f2019-01-03 17:50:52 -08001259 if (mTopPadding != topPadding) {
1260 mTopPadding = topPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001261 updateAlgorithmHeightAndPadding();
1262 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +02001263 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001264 mTopPaddingNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04001265 mNeedsAnimation = true;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001266 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001267 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -07001268 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001269 }
1270 }
1271
1272 /**
Selim Cinekbc243a92016-09-27 16:35:13 -07001273 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001274 *
Selim Cinekbc243a92016-09-27 16:35:13 -07001275 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001276 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001277 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbc243a92016-09-27 16:35:13 -07001278 public void setExpandedHeight(float height) {
1279 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001280 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001281 int minExpansionHeight = getMinExpansionHeight();
1282 if (height < minExpansionHeight) {
1283 mClipRect.left = 0;
1284 mClipRect.right = getWidth();
1285 mClipRect.top = 0;
1286 mClipRect.bottom = (int) height;
1287 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001288 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001289 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001290 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001291 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001292 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -07001293 float translationY;
1294 float appearEndPosition = getAppearEndPosition();
1295 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001296 float appearFraction = 1.0f;
shawnlin5be1f7c2018-05-21 20:50:54 +08001297 boolean appearing = height < appearEndPosition;
1298 mAmbientState.setAppearing(appearing);
1299 if (!appearing) {
Selim Cinekbc243a92016-09-27 16:35:13 -07001300 translationY = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +08001301 if (mShouldShowShelfOnly) {
1302 stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
1303 } else if (mQsExpanded) {
1304 int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
1305 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
1306 if (stackStartPosition <= stackEndPosition) {
1307 stackHeight = stackEndPosition;
1308 } else {
1309 stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
1310 stackEndPosition, mQsExpansionFraction);
1311 }
1312 } else {
1313 stackHeight = (int) height;
1314 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001315 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001316 appearFraction = getAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -07001317 if (appearFraction >= 0) {
1318 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
1319 appearFraction);
1320 } else {
1321 // This may happen when pushing up a heads up. We linearly push it up from the
1322 // start
1323 translationY = height - appearStartPosition + getExpandTranslationStart();
1324 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001325 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001326 stackHeight =
1327 getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001328 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
1329 } else {
1330 stackHeight = (int) (height - translationY);
1331 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001332 }
1333 if (stackHeight != mCurrentStackHeight) {
1334 mCurrentStackHeight = stackHeight;
1335 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +02001336 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001337 }
Selim Cinek94c2d822016-07-13 18:50:04 -07001338 setStackTranslation(translationY);
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001339 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
1340 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
1341 listener.accept(mExpandedHeight, appearFraction);
1342 }
Selim Cinekcafa87f2016-10-26 17:00:17 -07001343 }
1344
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001345 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001346 private void setRequestedClipBounds(Rect clipRect) {
1347 mRequestedClipBounds = clipRect;
1348 updateClipping();
1349 }
1350
Lucas Dupin60661a62018-04-12 10:50:13 -07001351 /**
1352 * Return the height of the content ignoring the footer.
1353 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001354 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -07001355 public int getIntrinsicContentHeight() {
1356 return mIntrinsicContentHeight;
1357 }
1358
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001359 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001360 public void updateClipping() {
1361 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
1362 && !mHeadsUpAnimatingAway;
Lucas Dupin64e2f572019-03-21 14:21:14 -07001363 boolean clipToOutline = false;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001364 if (mIsClipped != clipped) {
1365 mIsClipped = clipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001366 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08001367
Selim Cinek3d6ae232019-01-04 14:14:33 -08001368 if (!mAmbientPulseManager.hasNotifications()
1369 && mAmbientState.isFullyDark() && mShowDarkShelf) {
Lucas Dupin4798ea12018-11-05 19:26:24 -08001370 setClipBounds(null);
1371 } else if (mAmbientState.isDarkAtAll()) {
Lucas Dupin64e2f572019-03-21 14:21:14 -07001372 clipToOutline = true;
1373 invalidateOutline();
Lucas Dupin16cfe452018-02-08 13:14:50 -08001374 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001375 setClipBounds(mRequestedClipBounds);
1376 } else {
1377 setClipBounds(null);
1378 }
Lucas Dupin64e2f572019-03-21 14:21:14 -07001379
1380 setClipToOutline(clipToOutline);
Selim Cinek94c2d822016-07-13 18:50:04 -07001381 }
1382
1383 /**
1384 * @return The translation at the beginning when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001385 * Measured relative to the resting position.
Selim Cinek94c2d822016-07-13 18:50:04 -07001386 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001387 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001388 private float getExpandTranslationStart() {
Selim Cinek083f2142018-11-06 16:32:23 -08001389 return -mTopPadding + getMinExpansionHeight() - mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001390 }
1391
1392 /**
1393 * @return the position from where the appear transition starts when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001394 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001395 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001396 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001397 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001398 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001399 return mHeadsUpInset
1400 + getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -07001401 }
Selim Cinek48ff9b42016-11-09 19:31:51 -08001402 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001403 }
1404
1405 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001406 * @return the height of the top heads up notification when pinned. This is different from the
Jason Monke59dc402018-08-16 12:05:01 -04001407 * intrinsic height, which also includes whether the notification is system expanded and
1408 * is mainly used when dragging down from a heads up notification.
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001409 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001410 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001411 private int getTopHeadsUpPinnedHeight() {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001412 NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001413 if (topEntry == null) {
1414 return 0;
1415 }
Evan Laird94492852018-10-25 13:43:01 -04001416 ExpandableNotificationRow row = topEntry.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001417 if (row.isChildInGroup()) {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001418 final NotificationEntry groupSummary
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001419 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
1420 if (groupSummary != null) {
Evan Laird94492852018-10-25 13:43:01 -04001421 row = groupSummary.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001422 }
1423 }
1424 return row.getPinnedHeadsUpHeight();
1425 }
1426
1427 /**
Selim Cinek94c2d822016-07-13 18:50:04 -07001428 * @return the position from where the appear transition ends when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001429 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001430 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001431 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001432 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -07001433 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -07001434 int notGoneChildCount = getNotGoneChildCount();
Julia Reynolds34f14962018-05-03 12:40:20 +00001435 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001436 if (isHeadsUpTransition()
Selim Cinekebf42342017-07-13 15:46:10 +02001437 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001438 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +01001439 } else {
1440 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001441 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1442 appearPosition += mShelf.getIntrinsicHeight();
1443 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001444 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001445 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001446 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001447 }
1448 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001449 }
1450
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001451 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001452 private boolean isHeadsUpTransition() {
Gus Prevase2d6f042018-10-17 15:25:30 -04001453 NotificationSection firstVisibleSection = getFirstVisibleSection();
1454 return mTrackingHeadsUp && firstVisibleSection != null
Selim Cinek459aee32019-02-20 11:18:56 -08001455 && firstVisibleSection.getFirstVisibleChild().isAboveShelf();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001456 }
1457
Selim Cinek94c2d822016-07-13 18:50:04 -07001458 /**
1459 * @param height the height of the panel
1460 * @return the fraction of the appear animation that has been performed
1461 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001462 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001463 public float getAppearFraction(float height) {
1464 float appearEndPosition = getAppearEndPosition();
1465 float appearStartPosition = getAppearStartPosition();
1466 return (height - appearStartPosition)
1467 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001468 }
1469
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001470 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001471 public float getStackTranslation() {
1472 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001473 }
1474
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001475 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001476 private void setStackTranslation(float stackTranslation) {
1477 if (stackTranslation != mStackTranslation) {
1478 mStackTranslation = stackTranslation;
1479 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001480 requestChildrenUpdate();
1481 }
Selim Cinek67b22602014-03-10 15:40:16 +01001482 }
1483
1484 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001485 * 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 +01001486 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1487 *
1488 * @return either the layout height or the externally defined height, whichever is smaller
1489 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001490 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek343e6e22014-04-11 21:23:30 +02001491 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001492 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1493 }
1494
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001495 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek816c8e42015-11-19 12:00:45 -08001496 public int getFirstItemMinHeight() {
1497 final ExpandableView firstChild = getFirstChildNotGone();
1498 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001499 }
1500
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001501 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Jason Monk16ac3772016-02-10 15:39:21 -05001502 public void setQsContainer(ViewGroup qsContainer) {
1503 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001504 }
1505
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001506 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek684a4422015-04-15 16:18:39 -07001507 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001508 if (v instanceof ExpandableNotificationRow) {
1509 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001510 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001511 }
1512 return false;
1513 }
1514
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001515 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cineka59ecc32015-04-07 10:51:49 -07001516 private boolean isHeadsUp(View v) {
1517 if (v instanceof ExpandableNotificationRow) {
1518 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1519 return row.isHeadsUp();
1520 }
1521 return false;
1522 }
1523
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001524 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001525 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1526 getLocationOnScreen(mTempInt2);
1527 float localTouchY = touchY - mTempInt2[1];
1528
1529 ExpandableView closestChild = null;
1530 float minDist = Float.MAX_VALUE;
1531
1532 // find the view closest to the location, accounting for GONE views
1533 final int count = getChildCount();
1534 for (int childIdx = 0; childIdx < count; childIdx++) {
1535 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1536 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001537 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001538 continue;
1539 }
1540 float childTop = slidingChild.getTranslationY();
1541 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001542 float bottom = childTop + slidingChild.getActualHeight()
1543 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001544
1545 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1546 if (dist < minDist) {
1547 closestChild = slidingChild;
1548 minDist = dist;
1549 }
1550 }
1551 return closestChild;
1552 }
1553
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001554 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
1555 private ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001556 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1557
1558 }
1559
1560 /**
1561 * Get the child at a certain screen location.
1562 *
Jason Monke59dc402018-08-16 12:05:01 -04001563 * @param touchX the x coordinate
1564 * @param touchY the y coordinate
Selim Cinek34ed7c02017-09-08 15:03:12 -07001565 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1566 * @return the child at the given location.
1567 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001568 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek34ed7c02017-09-08 15:03:12 -07001569 private ExpandableView getChildAtPosition(float touchX, float touchY,
1570 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001571 // find the view under the pointer, accounting for GONE views
1572 final int count = getChildCount();
1573 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001574 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001575 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001576 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001577 continue;
1578 }
Selim Cinek89faff12014-06-19 16:29:04 -07001579 float childTop = slidingChild.getTranslationY();
1580 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001581 float bottom = childTop + slidingChild.getActualHeight()
1582 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001583
1584 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1585 // camera affordance).
1586 int left = 0;
1587 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001588
Selim Cinek34ed7c02017-09-08 15:03:12 -07001589 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001590 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001591 if (slidingChild instanceof ExpandableNotificationRow) {
1592 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Ned Burnsf81c4c42019-01-07 14:10:43 -05001593 NotificationEntry entry = row.getEntry();
Selim Cinek131c1e22015-05-11 19:04:49 -07001594 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Evan Laird94492852018-10-25 13:43:01 -04001595 && mHeadsUpManager.getTopEntry().getRow() != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001596 && mGroupManager.getGroupSummary(
Evan Laird94492852018-10-25 13:43:01 -04001597 mHeadsUpManager.getTopEntry().notification)
1598 != entry) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001599 continue;
1600 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001601 return row.getViewAtPosition(touchY - childTop);
1602 }
Selim Cinek67b22602014-03-10 15:40:16 +01001603 return slidingChild;
1604 }
1605 }
1606 return null;
1607 }
1608
Selim Cinek3d6ae232019-01-04 14:14:33 -08001609 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001610 getLocationOnScreen(mTempInt2);
1611 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001612 }
1613
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001614 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001615 public void setScrollingEnabled(boolean enable) {
1616 mScrollingEnabled = enable;
1617 }
1618
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001619 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001620 public void lockScrollTo(View v) {
1621 if (mForcedScroll == v) {
1622 return;
1623 }
1624 mForcedScroll = v;
1625 scrollTo(v);
1626 }
1627
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001628 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001629 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001630 ExpandableView expandableView = (ExpandableView) v;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001631 if (ANCHOR_SCROLLING) {
1632 // TODO
1633 } else {
1634 int positionInLinearLayout = getPositionInLinearLayout(v);
1635 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1636 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001637
Gus Prevas0fa58d62019-01-11 13:58:40 -05001638 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1639 // that it is not visible anymore.
1640 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1641 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1642 mDontReportNextOverScroll = true;
1643 animateScroll();
1644 return true;
1645 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001646 }
Ricky Waicd35def2016-05-03 11:07:07 +01001647 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001648 }
1649
Adrian Roos181385c2016-05-05 17:45:44 -04001650 /**
1651 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
Jason Monke59dc402018-08-16 12:05:01 -04001652 * the IME.
Adrian Roos181385c2016-05-05 17:45:44 -04001653 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001654 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos181385c2016-05-05 17:45:44 -04001655 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1656 return positionInLinearLayout + v.getIntrinsicHeight() +
felkachang529bfe62018-07-04 12:51:44 +08001657 getImeInset() - getHeight()
1658 + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
Adrian Roos181385c2016-05-05 17:45:44 -04001659 }
1660
Adrian Roos5153d4a2016-03-22 10:01:56 -07001661 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001662 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001663 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001664 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001665
Gus Prevas0fa58d62019-01-11 13:58:40 -05001666 if (ANCHOR_SCROLLING) {
1667 // TODO
1668 } else {
1669 int range = getScrollRange();
1670 if (mOwnScrollY > range) {
1671 // HACK: We're repeatedly getting staggered insets here while the IME is
1672 // animating away. To work around that we'll wait until things have settled.
1673 removeCallbacks(mReclamp);
1674 postDelayed(mReclamp, 50);
1675 } else if (mForcedScroll != null) {
1676 // The scroll was requested before we got the actual inset - in case we need
1677 // to scroll up some more do so now.
1678 scrollTo(mForcedScroll);
1679 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001680 }
1681 return insets;
1682 }
1683
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001684 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001685 private Runnable mReclamp = new Runnable() {
1686 @Override
1687 public void run() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001688 if (ANCHOR_SCROLLING) {
1689 // TODO
1690 } else {
1691 int range = getScrollRange();
1692 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1693 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001694 mDontReportNextOverScroll = true;
1695 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001696 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001697 }
1698 };
1699
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001700 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
1701 public void setExpandingEnabled(boolean enable) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001702 mExpandHelper.setEnabled(enable);
1703 }
1704
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001705 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001706 private boolean isScrollingEnabled() {
1707 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001708 }
1709
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001710 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07001711 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001712 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001713 }
1714
Selim Cinek67b22602014-03-10 15:40:16 +01001715 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001716 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001717 protected void onConfigurationChanged(Configuration newConfig) {
1718 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001719 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001720 float densityScale = getResources().getDisplayMetrics().density;
1721 mSwipeHelper.setDensityScale(densityScale);
1722 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1723 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1724 initView(getContext());
1725 }
1726
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001727 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dan Sandlereceda3d2014-07-21 15:35:01 -04001728 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001729 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1730 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001731 }
1732
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001733 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001734 private void snapViewIfNeeded(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001735 ExpandableNotificationRow child = entry.getRow();
dongwan0605.kim30637e42016-03-02 17:16:47 +09001736 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001737 // If the child is showing the notification menu snap to that
Evan Lairde55c6012019-03-13 12:54:37 -04001738 if (child.getProvider() != null) {
1739 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
1740 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
1741 }
dongwan0605.kim30637e42016-03-02 17:16:47 +09001742 }
1743
Selim Cinek67b22602014-03-10 15:40:16 +01001744 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001745 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001746 public ViewGroup getViewParentForNotification(NotificationEntry entry) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001747 return this;
1748 }
1749
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001750 /**
1751 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1752 *
1753 * @param deltaY The amount to scroll upwards, has to be positive.
1754 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001755 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001756 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001757 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001758 private float overScrollUp(int deltaY, int range) {
1759 deltaY = Math.max(deltaY, 0);
1760 float currentTopAmount = getCurrentOverScrollAmount(true);
1761 float newTopAmount = currentTopAmount - deltaY;
1762 if (currentTopAmount > 0) {
1763 setOverScrollAmount(newTopAmount, true /* onTop */,
1764 false /* animate */);
1765 }
1766 // Top overScroll might not grab all scrolling motion,
1767 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001768 if (ANCHOR_SCROLLING) {
1769 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1770 // TODO: once we're recycling this will need to check the adapter position of the child
1771 ExpandableView lastRow = getLastRowNotGone();
Gus Prevascdc98342019-01-14 14:29:44 -05001772 if (lastRow != null && !lastRow.isInShelf()) {
1773 float distanceToMax = Math.max(0, getMaxPositiveScrollAmount());
1774 if (scrollAmount > distanceToMax) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001775 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1776 // We overScroll on the bottom
1777 setOverScrolledPixels(currentBottomPixels + (scrollAmount - distanceToMax),
1778 false /* onTop */,
1779 false /* animate */);
1780 mScrollAnchorViewY -= distanceToMax;
1781 scrollAmount = 0f;
1782 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001783 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001784 return scrollAmount;
1785 } else {
1786 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1787 float newScrollY = mOwnScrollY + scrollAmount;
1788 if (newScrollY > range) {
1789 if (!mExpandedInThisMotion) {
1790 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1791 // We overScroll on the bottom
1792 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1793 false /* onTop */,
1794 false /* animate */);
1795 }
1796 setOwnScrollY(range);
1797 scrollAmount = 0.0f;
1798 }
1799 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001800 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001801 }
1802
1803 /**
1804 * Perform a scroll downward and adapt the overscroll amounts accordingly
1805 *
1806 * @param deltaY The amount to scroll downwards, has to be negative.
1807 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001808 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001809 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001810 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001811 private float overScrollDown(int deltaY) {
1812 deltaY = Math.min(deltaY, 0);
1813 float currentBottomAmount = getCurrentOverScrollAmount(false);
1814 float newBottomAmount = currentBottomAmount + deltaY;
1815 if (currentBottomAmount > 0) {
1816 setOverScrollAmount(newBottomAmount, false /* onTop */,
1817 false /* animate */);
1818 }
1819 // Bottom overScroll might not grab all scrolling motion,
1820 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001821 if (ANCHOR_SCROLLING) {
1822 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1823 // TODO: once we're recycling this will need to check the adapter position of the child
1824 ExpandableView firstChild = getFirstChildNotGone();
1825 float top = firstChild.getTranslationY();
1826 float distanceToTop = mScrollAnchorView.getTranslationY() - top - mScrollAnchorViewY;
1827 if (distanceToTop < -scrollAmount) {
1828 float currentTopPixels = getCurrentOverScrolledPixels(true);
1829 // We overScroll on the top
1830 setOverScrolledPixels(currentTopPixels + (-scrollAmount - distanceToTop),
1831 true /* onTop */,
1832 false /* animate */);
1833 mScrollAnchorView = firstChild;
1834 mScrollAnchorViewY = 0;
1835 scrollAmount = 0f;
1836 }
1837 return scrollAmount;
1838 } else {
1839 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1840 float newScrollY = mOwnScrollY + scrollAmount;
1841 if (newScrollY < 0) {
1842 float currentTopPixels = getCurrentOverScrolledPixels(true);
1843 // We overScroll on the top
1844 setOverScrolledPixels(currentTopPixels - newScrollY,
1845 true /* onTop */,
1846 false /* animate */);
1847 setOwnScrollY(0);
1848 scrollAmount = 0.0f;
1849 }
1850 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001851 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001852 }
1853
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001854 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001855 private void initVelocityTrackerIfNotExists() {
1856 if (mVelocityTracker == null) {
1857 mVelocityTracker = VelocityTracker.obtain();
1858 }
1859 }
1860
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001861 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001862 private void recycleVelocityTracker() {
1863 if (mVelocityTracker != null) {
1864 mVelocityTracker.recycle();
1865 mVelocityTracker = null;
1866 }
1867 }
1868
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001869 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001870 private void initOrResetVelocityTracker() {
1871 if (mVelocityTracker == null) {
1872 mVelocityTracker = VelocityTracker.obtain();
1873 } else {
1874 mVelocityTracker.clear();
1875 }
1876 }
1877
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001878 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001879 public void setFinishScrollingCallback(Runnable runnable) {
1880 mFinishScrollingCallback = runnable;
1881 }
1882
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001883 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9212de82017-02-06 16:04:28 -08001884 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001885 if (mScroller.computeScrollOffset()) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001886 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001887 int oldY = mLastScrollerY;
1888 int y = mScroller.getCurrY();
1889 int deltaY = y - oldY;
1890 if (deltaY != 0) {
1891 int maxNegativeScrollAmount = getMaxNegativeScrollAmount();
1892 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
1893 if ((maxNegativeScrollAmount < 0 && deltaY < maxNegativeScrollAmount)
1894 || (maxPositiveScrollAmount > 0 && deltaY > maxPositiveScrollAmount)) {
1895 // This frame takes us into overscroll, so set the max overscroll based on
1896 // the current velocity
1897 setMaxOverScrollFromCurrentVelocity();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001898 }
Gus Prevascdc98342019-01-14 14:29:44 -05001899 customOverScrollBy(deltaY, oldY, 0, (int) mMaxOverScroll);
1900 mLastScrollerY = y;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001901 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001902 } else {
1903 int oldY = mOwnScrollY;
1904 int y = mScroller.getCurrY();
Selim Cinek67b22602014-03-10 15:40:16 +01001905
Gus Prevas0fa58d62019-01-11 13:58:40 -05001906 if (oldY != y) {
1907 int range = getScrollRange();
1908 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
Gus Prevascdc98342019-01-14 14:29:44 -05001909 // This frame takes us into overscroll, so set the max overscroll based on
1910 // the current velocity
1911 setMaxOverScrollFromCurrentVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +01001912 }
Selim Cinek67b22602014-03-10 15:40:16 +01001913
Gus Prevas0fa58d62019-01-11 13:58:40 -05001914 if (mDontClampNextScroll) {
1915 range = Math.max(range, oldY);
1916 }
1917 customOverScrollBy(y - oldY, oldY, range,
1918 (int) (mMaxOverScroll));
Adrian Roos5153d4a2016-03-22 10:01:56 -07001919 }
Selim Cinek67b22602014-03-10 15:40:16 +01001920 }
1921
Gus Prevascdc98342019-01-14 14:29:44 -05001922 postOnAnimation(mReflingAndAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001923 } else {
1924 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001925 if (mFinishScrollingCallback != null) {
1926 mFinishScrollingCallback.run();
1927 }
Selim Cinek67b22602014-03-10 15:40:16 +01001928 }
1929 }
1930
Gus Prevascdc98342019-01-14 14:29:44 -05001931 private void setMaxOverScrollFromCurrentVelocity() {
1932 float currVelocity = mScroller.getCurrVelocity();
1933 if (currVelocity >= mMinimumVelocity) {
1934 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
Selim Cinek4195dd02014-05-19 18:16:14 +02001935 }
Gus Prevascdc98342019-01-14 14:29:44 -05001936 }
Selim Cinek4195dd02014-05-19 18:16:14 +02001937
Gus Prevascdc98342019-01-14 14:29:44 -05001938 /**
1939 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
1940 * would cause us to exceed the provided maximum overscroll, springs back instead.
1941 *
1942 * This method performs the determination of whether we're exceeding the overscroll and clamps
1943 * the scroll amount if so. The actual scrolling/overscrolling happens in
1944 * {@link #onCustomOverScrolled(int, boolean)} (absolute scrolling) or
1945 * {@link #onCustomOverScrolledBy(int, boolean)} (anchor scrolling).
1946 *
1947 * @param deltaY The (signed) number of pixels to scroll.
1948 * @param scrollY The current scroll position (absolute scrolling only).
1949 * @param scrollRangeY The maximum allowable scroll position (absolute scrolling only).
1950 * @param maxOverScrollY The current (unsigned) limit on number of pixels to overscroll by.
1951 */
Selim Cinek4195dd02014-05-19 18:16:14 +02001952 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevascdc98342019-01-14 14:29:44 -05001953 private void customOverScrollBy(int deltaY, int scrollY, int scrollRangeY, int maxOverScrollY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001954 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001955 boolean clampedY = false;
1956 if (deltaY < 0) {
1957 int maxScrollAmount = getMaxNegativeScrollAmount();
1958 if (maxScrollAmount > Integer.MIN_VALUE) {
1959 maxScrollAmount -= maxOverScrollY;
1960 if (deltaY < maxScrollAmount) {
1961 deltaY = maxScrollAmount;
1962 clampedY = true;
1963 }
1964 }
1965 } else {
1966 int maxScrollAmount = getMaxPositiveScrollAmount();
1967 if (maxScrollAmount < Integer.MAX_VALUE) {
1968 maxScrollAmount += maxOverScrollY;
1969 if (deltaY > maxScrollAmount) {
1970 deltaY = maxScrollAmount;
1971 clampedY = true;
1972 }
1973 }
1974 }
1975 onCustomOverScrolledBy(deltaY, clampedY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05001976 } else {
1977 int newScrollY = scrollY + deltaY;
1978 final int top = -maxOverScrollY;
1979 final int bottom = maxOverScrollY + scrollRangeY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001980
Gus Prevas0fa58d62019-01-11 13:58:40 -05001981 boolean clampedY = false;
1982 if (newScrollY > bottom) {
1983 newScrollY = bottom;
1984 clampedY = true;
1985 } else if (newScrollY < top) {
1986 newScrollY = top;
1987 clampedY = true;
1988 }
Selim Cinek4195dd02014-05-19 18:16:14 +02001989
Gus Prevas0fa58d62019-01-11 13:58:40 -05001990 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001991 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001992 }
1993
1994 /**
1995 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1996 * overscroll effect based on numPixels. By default this will also cancel animations on the
1997 * same overScroll edge.
1998 *
1999 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
2000 * the rubber-banding logic.
Jason Monke59dc402018-08-16 12:05:01 -04002001 * @param onTop Should the effect be applied on top of the scroller.
2002 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002003 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002004 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002005 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07002006 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002007 }
2008
2009 /**
2010 * Set the effective overScroll amount which will be directly reflected in the layout.
2011 * By default this will also cancel animations on the same overScroll edge.
2012 *
Jason Monke59dc402018-08-16 12:05:01 -04002013 * @param amount The amount to overScroll by.
2014 * @param onTop Should the effect be applied on top of the scroller.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002015 * @param animate Should an animation be performed.
2016 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002017
2018 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002019 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
2020 setOverScrollAmount(amount, onTop, animate, true);
2021 }
2022
2023 /**
2024 * Set the effective overScroll amount which will be directly reflected in the layout.
2025 *
Jason Monke59dc402018-08-16 12:05:01 -04002026 * @param amount The amount to overScroll by.
2027 * @param onTop Should the effect be applied on top of the scroller.
2028 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002029 * @param cancelAnimators Should running animations be cancelled.
2030 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002031 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002032 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
2033 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002034 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
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.
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002043 * @param cancelAnimators Should running animations be cancelled.
Jason Monke59dc402018-08-16 12:05:01 -04002044 * @param isRubberbanded The value which will be passed to
2045 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002046 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002047 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002048 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
2049 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002050 if (cancelAnimators) {
2051 mStateAnimator.cancelOverScrollAnimators(onTop);
2052 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002053 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002054 }
2055
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002056 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002057 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
2058 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002059 amount = Math.max(0, amount);
2060 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002061 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002062 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07002063 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002064 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002065 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002066 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002067 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002068 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002069 }
2070 }
2071
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002072 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002073 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002074 mExpandHelper.onlyObserveMovements(amount > 1.0f);
2075 if (mDontReportNextOverScroll) {
2076 mDontReportNextOverScroll = false;
2077 return;
2078 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02002079 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002080 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002081 }
2082 }
2083
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002084 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi290600a2014-05-30 17:02:20 +02002085 public void setOverscrollTopChangedListener(
2086 OnOverscrollTopChangedListener overscrollTopChangedListener) {
2087 mOverscrollTopChangedListener = overscrollTopChangedListener;
2088 }
2089
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002090 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002091 public float getCurrentOverScrollAmount(boolean top) {
2092 return mAmbientState.getOverScrollAmount(top);
2093 }
2094
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002095 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002096 public float getCurrentOverScrolledPixels(boolean top) {
Jason Monke59dc402018-08-16 12:05:01 -04002097 return top ? mOverScrolledTopPixels : mOverScrolledBottomPixels;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002098 }
2099
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002100 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002101 private void setOverScrolledPixels(float amount, boolean onTop) {
2102 if (onTop) {
2103 mOverScrolledTopPixels = amount;
2104 } else {
2105 mOverScrolledBottomPixels = amount;
2106 }
2107 }
2108
Gus Prevascdc98342019-01-14 14:29:44 -05002109 /**
2110 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
2111 * would cause us to exceed the provided maximum overscroll, springs back instead.
2112 *
2113 * @param deltaY The (signed) number of pixels to scroll.
2114 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2115 * the overscroll limit.
2116 */
2117 private void onCustomOverScrolledBy(int deltaY, boolean clampedY) {
2118 assert ANCHOR_SCROLLING;
Gus Prevas0fa58d62019-01-11 13:58:40 -05002119 mScrollAnchorViewY -= deltaY;
2120 // Treat animating scrolls differently; see #computeScroll() for why.
2121 if (!mScroller.isFinished()) {
Gus Prevascdc98342019-01-14 14:29:44 -05002122 if (clampedY) {
2123 springBack();
2124 } else {
2125 float overScrollTop = getCurrentOverScrollAmount(true /* top */);
2126 if (isScrolledToTop() && mScrollAnchorViewY > 0) {
2127 notifyOverscrollTopListener(mScrollAnchorViewY,
2128 isRubberbanded(true /* onTop */));
2129 } else {
2130 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true /* onTop */));
2131 }
2132 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002133 }
2134 updateScrollAnchor();
2135 updateOnScrollChange();
2136 }
2137
Gus Prevascdc98342019-01-14 14:29:44 -05002138 /**
2139 * Scrolls to the given position, overscrolling if needed. If called during a fling and the
2140 * position exceeds the provided maximum overscroll, springs back instead.
2141 *
2142 * @param scrollY The target scroll position.
2143 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2144 * the overscroll limit.
2145 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002146 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek9212de82017-02-06 16:04:28 -08002147 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002148 assert !ANCHOR_SCROLLING;
Selim Cinek67b22602014-03-10 15:40:16 +01002149 // Treat animating scrolls differently; see #computeScroll() for why.
2150 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07002151 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002152 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002153 springBack();
2154 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02002155 float overScrollTop = getCurrentOverScrollAmount(true);
2156 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002157 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002158 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002159 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002160 }
Selim Cinek67b22602014-03-10 15:40:16 +01002161 }
Selim Cinek67b22602014-03-10 15:40:16 +01002162 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08002163 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002164 }
2165 }
2166
Gus Prevascdc98342019-01-14 14:29:44 -05002167 /**
2168 * Springs back from an overscroll by stopping the {@link #mScroller} and animating the
2169 * overscroll amount back to zero.
2170 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002171 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002172 private void springBack() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002173 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002174 boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0;
2175 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
2176 boolean overscrolledBottom = maxPositiveScrollAmount < 0;
2177 if (overScrolledTop || overscrolledBottom) {
2178 float newAmount;
2179 if (overScrolledTop) {
2180 newAmount = mScrollAnchorViewY;
2181 mScrollAnchorViewY = 0;
2182 mDontReportNextOverScroll = true;
2183 } else {
2184 newAmount = -maxPositiveScrollAmount;
2185 mScrollAnchorViewY -= maxPositiveScrollAmount;
2186 }
2187 setOverScrollAmount(newAmount, overScrolledTop, false);
2188 setOverScrollAmount(0.0f, overScrolledTop, true);
2189 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002190 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002191 } else {
2192 int scrollRange = getScrollRange();
2193 boolean overScrolledTop = mOwnScrollY <= 0;
2194 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2195 if (overScrolledTop || overScrolledBottom) {
2196 boolean onTop;
2197 float newAmount;
2198 if (overScrolledTop) {
2199 onTop = true;
2200 newAmount = -mOwnScrollY;
2201 setOwnScrollY(0);
2202 mDontReportNextOverScroll = true;
2203 } else {
2204 onTop = false;
2205 newAmount = mOwnScrollY - scrollRange;
2206 setOwnScrollY(scrollRange);
2207 }
2208 setOverScrollAmount(newAmount, onTop, false);
2209 setOverScrollAmount(0.0f, onTop, true);
2210 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002211 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002212 }
2213 }
2214
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002215 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek67b22602014-03-10 15:40:16 +01002216 private int getScrollRange() {
felkachang529bfe62018-07-04 12:51:44 +08002217 // In current design, it only use the top HUN to treat all of HUNs
2218 // although there are more than one HUNs
2219 int contentHeight = mContentHeight;
2220 if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
2221 contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
2222 }
2223 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002224 int imeInset = getImeInset();
felkachang529bfe62018-07-04 12:51:44 +08002225 scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
Selim Cineka424c502016-04-05 13:07:54 -07002226 return scrollRange;
2227 }
2228
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002229 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cineka424c502016-04-05 13:07:54 -07002230 private int getImeInset() {
2231 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002232 }
2233
Selim Cinek343e6e22014-04-11 21:23:30 +02002234 /**
2235 * @return the first child which has visibility unequal to GONE
2236 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002237 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb55386d2015-12-16 17:26:49 -08002238 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002239 int childCount = getChildCount();
2240 for (int i = 0; i < childCount; i++) {
2241 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002242 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002243 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002244 }
2245 }
2246 return null;
2247 }
2248
Selim Cinek4a1ac842014-05-01 15:51:58 +02002249 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002250 * @return the child before the given view which has visibility unequal to GONE
2251 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002252 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002253 public ExpandableView getViewBeforeView(ExpandableView view) {
2254 ExpandableView previousView = null;
2255 int childCount = getChildCount();
2256 for (int i = 0; i < childCount; i++) {
2257 View child = getChildAt(i);
2258 if (child == view) {
2259 return previousView;
2260 }
2261 if (child.getVisibility() != View.GONE) {
2262 previousView = (ExpandableView) child;
2263 }
2264 }
2265 return null;
2266 }
2267
2268 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002269 * @return The first child which has visibility unequal to GONE which is currently below the
Jason Monke59dc402018-08-16 12:05:01 -04002270 * given translationY or equal to it.
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002271 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002272 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekef8c2252017-02-10 14:52:18 -08002273 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002274 int childCount = getChildCount();
2275 for (int i = 0; i < childCount; i++) {
2276 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002277 if (child.getVisibility() == View.GONE) {
2278 continue;
2279 }
2280 float rowTranslation = child.getTranslationY();
2281 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002282 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002283 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2284 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2285 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2286 List<ExpandableNotificationRow> notificationChildren =
2287 row.getNotificationChildren();
2288 for (int childIndex = 0; childIndex < notificationChildren.size();
2289 childIndex++) {
2290 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2291 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2292 return rowChild;
2293 }
2294 }
2295 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002296 }
2297 }
2298 return null;
2299 }
2300
2301 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002302 * @return the last child which has visibility unequal to GONE
2303 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002304 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002305 public ExpandableView getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002306 int childCount = getChildCount();
2307 for (int i = childCount - 1; i >= 0; i--) {
2308 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002309 if (child.getVisibility() != View.GONE && child != mShelf) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002310 return (ExpandableView) child;
Selim Cinek4a1ac842014-05-01 15:51:58 +02002311 }
2312 }
2313 return null;
2314 }
2315
Gus Prevas0fa58d62019-01-11 13:58:40 -05002316 private ExpandableNotificationRow getLastRowNotGone() {
2317 int childCount = getChildCount();
2318 for (int i = childCount - 1; i >= 0; i--) {
2319 View child = getChildAt(i);
2320 if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
2321 return (ExpandableNotificationRow) child;
2322 }
2323 }
2324 return null;
2325 }
2326
Jorim Jaggi069cd032014-05-15 03:09:01 +02002327 /**
2328 * @return the number of children which have visibility unequal to GONE
2329 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002330 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi069cd032014-05-15 03:09:01 +02002331 public int getNotGoneChildCount() {
2332 int childCount = getChildCount();
2333 int count = 0;
2334 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002335 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002336 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002337 count++;
2338 }
2339 }
2340 return count;
2341 }
2342
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002343 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01002344 private void updateContentHeight() {
2345 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002346 float previousPaddingRequest = mPaddingBetweenElements;
2347 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002348 int numShownItems = 0;
2349 boolean finish = false;
Selim Cinek5040f2e2019-02-14 18:22:42 -08002350 int maxDisplayedNotifications = mMaxDisplayedNotifications;
Adrian Roos7a9551a2017-01-11 12:27:49 -08002351
Selim Cinek67b22602014-03-10 15:40:16 +01002352 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002353 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002354 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002355 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002356 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002357 boolean limitReached = maxDisplayedNotifications != -1
2358 && numShownItems >= maxDisplayedNotifications;
Selim Cinek5040f2e2019-02-14 18:22:42 -08002359 if (limitReached) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002360 expandableView = mShelf;
2361 finish = true;
2362 }
Selim Cinek42357e02016-02-24 18:48:01 -08002363 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002364 float padding;
2365 if (increasedPaddingAmount >= 0.0f) {
2366 padding = (int) NotificationUtils.interpolate(
2367 previousPaddingRequest,
2368 mIncreasedPaddingBetweenElements,
2369 increasedPaddingAmount);
2370 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002371 mPaddingBetweenElements,
2372 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002373 increasedPaddingAmount);
2374 } else {
2375 int ownPadding = (int) NotificationUtils.interpolate(
2376 0,
2377 mPaddingBetweenElements,
2378 1.0f + increasedPaddingAmount);
2379 if (previousPaddingAmount > 0.0f) {
2380 padding = (int) NotificationUtils.interpolate(
2381 ownPadding,
2382 mIncreasedPaddingBetweenElements,
2383 previousPaddingAmount);
2384 } else {
2385 padding = ownPadding;
2386 }
2387 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002388 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002389 if (height != 0) {
2390 height += padding;
2391 }
2392 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002393 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002394 numShownItems++;
2395 if (finish) {
2396 break;
2397 }
Selim Cinek67b22602014-03-10 15:40:16 +01002398 }
2399 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002400 mIntrinsicContentHeight = height;
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002401
Lucas Dupin00be88f2019-01-03 17:50:52 -08002402 mContentHeight = height + mTopPadding + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002403 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002404 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002405 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002406 }
2407
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002408 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burnsf81c4c42019-01-07 14:10:43 -05002409 private boolean isPulsing(NotificationEntry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002410 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002411 }
2412
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002413 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002414 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbe2c4432017-05-30 12:11:09 -07002415 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002416 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002417 }
2418
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002419 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07002420 private void updateScrollability() {
Riddle Hsu065c01c2018-05-10 23:14:19 +08002421 boolean scrollable = !mQsExpanded && getScrollRange() > 0;
Selim Cinekc22fff62016-05-20 12:44:30 -07002422 if (scrollable != mScrollable) {
2423 mScrollable = scrollable;
2424 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002425 updateForwardAndBackwardScrollability();
2426 }
2427 }
2428
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002429 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef406062016-09-29 17:33:13 -07002430 private void updateForwardAndBackwardScrollability() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002431 boolean forwardScrollable = mScrollable && !isScrolledToBottom();
2432 boolean backwardsScrollable = mScrollable && !isScrolledToTop();
Selim Cinekef406062016-09-29 17:33:13 -07002433 boolean changed = forwardScrollable != mForwardScrollable
2434 || backwardsScrollable != mBackwardScrollable;
2435 mForwardScrollable = forwardScrollable;
2436 mBackwardScrollable = backwardsScrollable;
2437 if (changed) {
2438 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002439 }
Selim Cinek67b22602014-03-10 15:40:16 +01002440 }
2441
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002442 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002443 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002444 // No need to update the background color if it's not being drawn.
Lucas Dupin16cfe452018-02-08 13:14:50 -08002445 if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002446 return;
2447 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002448
Selim Cinek6811d722016-01-19 17:53:12 -08002449 updateBackgroundBounds();
Gus Prevase2d6f042018-10-17 15:25:30 -04002450 if (didSectionBoundsChange()) {
2451 boolean animate = mAnimateNextSectionBoundsChange || mAnimateNextBackgroundTop
2452 || mAnimateNextBackgroundBottom || areSectionBoundsAnimating();
Selim Cinek54680902016-10-19 16:49:44 -07002453 if (!isExpanded()) {
2454 abortBackgroundAnimators();
2455 animate = false;
2456 }
2457 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002458 startBackgroundAnimation();
2459 } else {
Gus Prevase2d6f042018-10-17 15:25:30 -04002460 for (NotificationSection section : mSections) {
2461 section.resetCurrentBounds();
2462 }
Lucas Dupin90a38dd2018-09-05 09:37:37 -07002463 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002464 }
2465 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002466 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002467 }
Selim Cinek614576e2016-01-20 10:54:09 -08002468 mAnimateNextBackgroundTop = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04002469 mAnimateNextBackgroundBottom = false;
2470 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08002471 }
2472
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002473 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek54680902016-10-19 16:49:44 -07002474 private void abortBackgroundAnimators() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002475 for (NotificationSection section : mSections) {
2476 section.cancelAnimators();
Selim Cinek54680902016-10-19 16:49:44 -07002477 }
2478 }
2479
Gus Prevase2d6f042018-10-17 15:25:30 -04002480 private boolean didSectionBoundsChange() {
2481 for (NotificationSection section : mSections) {
2482 if (section.didBoundsChange()) {
2483 return true;
2484 }
2485 }
2486 return false;
2487 }
2488
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002489 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevase2d6f042018-10-17 15:25:30 -04002490 private boolean areSectionBoundsAnimating() {
2491 for (NotificationSection section : mSections) {
2492 if (section.areBoundsAnimating()) {
2493 return true;
2494 }
2495 }
2496 return false;
Selim Cinek614576e2016-01-20 10:54:09 -08002497 }
2498
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002499 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08002500 private void startBackgroundAnimation() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002501 // TODO(kprevas): do we still need separate fields for top/bottom?
2502 // or can each section manage its own animation state?
2503 NotificationSection firstVisibleSection = getFirstVisibleSection();
2504 NotificationSection lastVisibleSection = getLastVisibleSection();
2505 for (NotificationSection section : mSections) {
2506 section.startBackgroundAnimation(
2507 section == firstVisibleSection
2508 ? mAnimateNextBackgroundTop
2509 : mAnimateNextSectionBoundsChange,
2510 section == lastVisibleSection
2511 ? mAnimateNextBackgroundBottom
2512 : mAnimateNextSectionBoundsChange);
Selim Cinek614576e2016-01-20 10:54:09 -08002513 }
Selim Cinek6811d722016-01-19 17:53:12 -08002514 }
2515
2516 /**
2517 * Update the background bounds to the new desired bounds
2518 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002519 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002520 private void updateBackgroundBounds() {
shawnlin27716722019-04-17 20:09:45 +08002521 int left = mSidePaddings;
2522 int right = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -04002523 for (NotificationSection section : mSections) {
2524 section.getBounds().left = left;
2525 section.getBounds().right = right;
2526 }
shawnlin3a2a2e22018-05-04 17:09:50 +08002527
Selim Cinek614576e2016-01-20 10:54:09 -08002528 if (!mIsExpanded) {
Gus Prevase2d6f042018-10-17 15:25:30 -04002529 for (NotificationSection section : mSections) {
2530 section.getBounds().top = 0;
2531 section.getBounds().bottom = 0;
2532 }
Selim Cinek1791f502016-10-07 17:38:03 -04002533 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002534 }
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002535 int minTopPosition = 0;
Gus Prevase2d6f042018-10-17 15:25:30 -04002536 NotificationSection lastSection = getLastVisibleSection();
Selim Cinek355652a2016-12-07 13:32:12 -08002537 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002538 minTopPosition = (int) (mTopPadding + mStackTranslation);
2539 } else if (lastSection == null) {
2540 minTopPosition = mTopPadding;
Selim Cinek3776fe02016-02-04 13:32:43 -08002541 }
Selim Cinekae55d832019-02-22 17:43:43 -08002542 boolean shiftPulsingWithFirst = mAmbientPulseManager.getAllEntries().count() <= 1;
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002543 for (NotificationSection section : mSections) {
2544 int minBottomPosition = minTopPosition;
2545 if (section == lastSection) {
2546 // We need to make sure the section goes all the way to the shelf
Selim Cinek3a1d2742019-03-11 18:38:29 -07002547 minBottomPosition = (int) (ViewState.getFinalTranslationY(mShelf)
2548 + mShelf.getIntrinsicHeight());
Gus Prevase83700cb2018-12-14 11:42:51 -05002549 }
Selim Cinekae55d832019-02-22 17:43:43 -08002550 minTopPosition = section.updateBounds(minTopPosition, minBottomPosition,
2551 shiftPulsingWithFirst);
2552 shiftPulsingWithFirst = false;
Gus Prevase83700cb2018-12-14 11:42:51 -05002553 }
Selim Cinek614576e2016-01-20 10:54:09 -08002554 }
2555
Gus Prevase2d6f042018-10-17 15:25:30 -04002556 private NotificationSection getFirstVisibleSection() {
2557 for (NotificationSection section : mSections) {
2558 if (section.getFirstVisibleChild() != null) {
2559 return section;
2560 }
2561 }
2562 return null;
2563 }
2564
2565 private NotificationSection getLastVisibleSection() {
2566 for (int i = mSections.length - 1; i >= 0; i--) {
2567 NotificationSection section = mSections[i];
2568 if (section.getLastVisibleChild() != null) {
2569 return section;
2570 }
2571 }
2572 return null;
2573 }
2574
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002575 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002576 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002577 int childCount = getChildCount();
2578 for (int i = childCount - 1; i >= 0; i--) {
2579 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002580 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2581 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002582 return (ActivatableNotificationView) child;
2583 }
2584 }
2585 return null;
2586 }
2587
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002588 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002589 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002590 int childCount = getChildCount();
2591 for (int i = 0; i < childCount; i++) {
2592 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002593 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2594 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002595 return (ActivatableNotificationView) child;
2596 }
2597 }
2598 return null;
2599 }
2600
Selim Cinek67b22602014-03-10 15:40:16 +01002601 /**
2602 * Fling the scroll view
2603 *
2604 * @param velocityY The initial velocity in the Y direction. Positive
2605 * numbers mean that the finger/cursor is moving down the screen,
2606 * which means we want to scroll towards the top.
2607 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002608 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Muyuan Li26e30ae2016-04-11 17:31:42 -07002609 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002610 if (getChildCount() > 0) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002611 float topAmount = getCurrentOverScrollAmount(true);
2612 float bottomAmount = getCurrentOverScrollAmount(false);
2613 if (velocityY < 0 && topAmount > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002614 if (ANCHOR_SCROLLING) {
2615 mScrollAnchorViewY += topAmount;
2616 } else {
2617 setOwnScrollY(mOwnScrollY - (int) topAmount);
2618 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002619 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002620 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002621 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002622 * mOverflingDistance + topAmount;
2623 } else if (velocityY > 0 && bottomAmount > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002624 if (ANCHOR_SCROLLING) {
2625 mScrollAnchorViewY -= bottomAmount;
2626 } else {
2627 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
2628 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002629 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002630 mMaxOverScroll = Math.abs(velocityY) / 1000f
2631 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
Jason Monke59dc402018-08-16 12:05:01 -04002632 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002633 } else {
2634 // it will be set once we reach the boundary
2635 mMaxOverScroll = 0.0f;
2636 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002637 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002638 flingScroller(velocityY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05002639 } else {
2640 int scrollRange = getScrollRange();
2641 int minScrollY = Math.max(0, scrollRange);
2642 if (mExpandedInThisMotion) {
2643 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2644 }
2645 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2646 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek94ab18c2016-02-25 12:35:51 -08002647 }
Selim Cinek67b22602014-03-10 15:40:16 +01002648
Selim Cinek9212de82017-02-06 16:04:28 -08002649 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002650 }
2651 }
2652
Selim Cinek1408eb52014-06-02 14:45:38 +02002653 /**
Gus Prevascdc98342019-01-14 14:29:44 -05002654 * Flings the overscroller with the given velocity (anchor-based scrolling).
2655 *
2656 * Because anchor-based scrolling can't track the current scroll position, the overscroller is
2657 * always started at startY = 0, and we interpret the positions it computes as relative to the
2658 * start of the scroll.
2659 */
2660 private void flingScroller(int velocityY) {
2661 assert ANCHOR_SCROLLING;
2662 mIsScrollerBoundSet = false;
2663 maybeFlingScroller(velocityY, true /* always fling */);
2664 }
2665
2666 private void maybeFlingScroller(int velocityY, boolean alwaysFling) {
2667 assert ANCHOR_SCROLLING;
2668 // Attempt to determine the maximum amount to scroll before we reach the end.
2669 // If the first view is not materialized (for an upwards scroll) or the last view is either
2670 // not materialized or is pinned to the shade (for a downwards scroll), we don't know this
2671 // amount, so we do an unbounded fling and rely on {@link #maybeReflingScroller()} to update
2672 // the scroller once we approach the start/end of the list.
2673 int minY = Integer.MIN_VALUE;
2674 int maxY = Integer.MAX_VALUE;
2675 if (velocityY < 0) {
2676 minY = getMaxNegativeScrollAmount();
2677 if (minY > Integer.MIN_VALUE) {
2678 mIsScrollerBoundSet = true;
2679 }
2680 } else {
2681 maxY = getMaxPositiveScrollAmount();
2682 if (maxY < Integer.MAX_VALUE) {
2683 mIsScrollerBoundSet = true;
2684 }
2685 }
2686 if (mIsScrollerBoundSet || alwaysFling) {
2687 mLastScrollerY = 0;
2688 // x velocity is set to 1 to avoid overscroller bug
2689 mScroller.fling(0, 0, 1, velocityY, 0, 0, minY, maxY, 0,
2690 mExpandedInThisMotion && !isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2);
2691 }
2692 }
2693
2694 /**
2695 * Returns the maximum number of pixels we can scroll in the positive direction (downwards)
2696 * before reaching the bottom of the list (discounting overscroll).
2697 *
2698 * If the return value is negative then we have overscrolled; this is a transient state which
2699 * should immediately be handled by adjusting the anchor position and adding the extra space to
2700 * the bottom overscroll amount.
2701 *
2702 * If we don't know how many pixels we have left to scroll (because the last row has not been
2703 * materialized, or it's in the shelf so it doesn't have its "natural" position), we return
2704 * {@link Integer#MAX_VALUE}.
2705 */
2706 private int getMaxPositiveScrollAmount() {
2707 assert ANCHOR_SCROLLING;
2708 // TODO: once we're recycling we need to check the adapter position of the last child.
2709 ExpandableNotificationRow lastRow = getLastRowNotGone();
2710 if (mScrollAnchorView != null && lastRow != null && !lastRow.isInShelf()) {
2711 // distance from bottom of last child to bottom of notifications area is:
2712 // distance from bottom of last child
2713 return (int) (lastRow.getTranslationY() + lastRow.getActualHeight()
2714 // to top of anchor view
2715 - mScrollAnchorView.getTranslationY()
2716 // plus distance from anchor view to top of notifications area
2717 + mScrollAnchorViewY
2718 // minus height of notifications area.
2719 - (mMaxLayoutHeight - getIntrinsicPadding() - mFooterView.getActualHeight()));
2720 } else {
2721 return Integer.MAX_VALUE;
2722 }
2723 }
2724
2725 /**
2726 * Returns the maximum number of pixels (as a negative number) we can scroll in the negative
2727 * direction (upwards) before reaching the top of the list (discounting overscroll).
2728 *
2729 * If the return value is positive then we have overscrolled; this is a transient state which
2730 * should immediately be handled by adjusting the anchor position and adding the extra space to
2731 * the top overscroll amount.
2732 *
2733 * If we don't know how many pixels we have left to scroll (because the first row has not been
2734 * materialized), we return {@link Integer#MIN_VALUE}.
2735 */
2736 private int getMaxNegativeScrollAmount() {
2737 assert ANCHOR_SCROLLING;
2738 // TODO: once we're recycling we need to check the adapter position of the first child.
2739 ExpandableView firstChild = getFirstChildNotGone();
2740 if (mScrollAnchorView != null && firstChild != null) {
2741 // distance from top of first child to top of notifications area is:
2742 // distance from top of anchor view
2743 return (int) -(mScrollAnchorView.getTranslationY()
2744 // to top of first child
2745 - firstChild.getTranslationY()
2746 // minus distance from top of anchor view to top of notifications area.
2747 - mScrollAnchorViewY);
2748 } else {
2749 return Integer.MIN_VALUE;
2750 }
2751 }
2752
2753 /**
2754 * During a fling, if we were unable to set the bounds of the fling due to the top/bottom view
2755 * not being materialized or being pinned to the shelf, we need to check on every frame if we're
2756 * able to set the bounds. If we are, we fling the scroller again with the newly computed
2757 * bounds.
2758 */
2759 private void maybeReflingScroller() {
2760 if (!mIsScrollerBoundSet) {
2761 // Because mScroller is a flywheel scroller, we fling with the minimum possible
2762 // velocity to establish direction, so as not to perceptibly affect the velocity.
2763 maybeFlingScroller((int) Math.signum(mScroller.getCurrVelocity()),
2764 false /* alwaysFling */);
2765 }
2766 }
2767
2768 /**
Selim Cinek1408eb52014-06-02 14:45:38 +02002769 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2770 * overScroll view (i.e QS).
2771 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002772 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02002773 private boolean shouldOverScrollFling(int initialVelocity) {
2774 float topOverScroll = getCurrentOverScrollAmount(true);
2775 return mScrolledToTopOnFirstDown
2776 && !mExpandedInThisMotion
2777 && topOverScroll > mMinTopOverScrollToEscape
2778 && initialVelocity > 0;
2779 }
2780
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002781 /**
2782 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2783 * account.
2784 *
Jason Monke59dc402018-08-16 12:05:01 -04002785 * @param qsHeight the top padding imposed by the quick settings panel
2786 * @param animate whether to animate the change
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002787 * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
2788 * {@code qsHeight} is the final top padding
2789 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002790 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jason Monk16ac3772016-02-10 15:39:21 -05002791 public void updateTopPadding(float qsHeight, boolean animate,
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002792 boolean ignoreIntrinsicPadding) {
Selim Cinekbc243a92016-09-27 16:35:13 -07002793 int topPadding = (int) qsHeight;
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002794 int minStackHeight = getLayoutMinHeight();
Selim Cinekbc243a92016-09-27 16:35:13 -07002795 if (topPadding + minStackHeight > getHeight()) {
2796 mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
Selim Cinek1408eb52014-06-02 14:45:38 +02002797 } else {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002798 mTopPaddingOverflow = 0;
Selim Cinek1408eb52014-06-02 14:45:38 +02002799 }
Selim Cinekbc243a92016-09-27 16:35:13 -07002800 setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002801 animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002802 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002803 }
2804
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002805 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
shawnlin8e4e92c2018-04-12 18:47:24 +08002806 public void setMaxTopPadding(int maxTopPadding) {
2807 mMaxTopPadding = maxTopPadding;
2808 }
2809
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002810 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002811 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002812 if (isHeadsUpTransition()) {
2813 return getTopHeadsUpPinnedHeight();
2814 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002815 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002816 }
2817
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002818 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002819 public float getTopPaddingOverflow() {
2820 return mTopPaddingOverflow;
2821 }
2822
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002823 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002824 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002825 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002826 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002827 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002828 int shelfHeight = 0;
Gus Prevase2d6f042018-10-17 15:25:30 -04002829 if (getLastVisibleSection() != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002830 shelfHeight = mShelf.getIntrinsicHeight();
2831 }
2832 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002833 }
2834
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002835 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02002836 private int clampPadding(int desiredPadding) {
2837 return Math.max(desiredPadding, mIntrinsicPadding);
2838 }
2839
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002840 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfed1ab62014-06-17 14:10:33 -07002841 private float getRubberBandFactor(boolean onTop) {
2842 if (!onTop) {
2843 return RUBBER_BAND_FACTOR_NORMAL;
2844 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002845 if (mExpandedInThisMotion) {
2846 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002847 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002848 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2849 } else if (mScrolledToTopOnFirstDown) {
2850 return 1.0f;
2851 }
2852 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002853 }
2854
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002855 /**
2856 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2857 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2858 * overscroll view (e.g. expand QS).
2859 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002860 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002861 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002862 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002863 || !mScrolledToTopOnFirstDown;
2864 }
2865
Selim Cinek67b22602014-03-10 15:40:16 +01002866
Selim Cinek67b22602014-03-10 15:40:16 +01002867
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002868 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef5127e2015-12-21 16:55:58 -08002869 public void setChildTransferInProgress(boolean childTransferInProgress) {
2870 mChildTransferInProgress = childTransferInProgress;
2871 }
2872
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002873 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Christoph Studer068f5922014-04-08 17:43:07 -04002874 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002875 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002876 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002877 // we only call our internal methods if this is actually a removal and not just a
2878 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002879 if (!mChildTransferInProgress) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002880 onViewRemovedInternal((ExpandableView) child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002881 }
2882 }
2883
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002884 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002885 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -05002886 public void cleanUpViewStateForEntry(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04002887 View child = entry.getRow();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04002888 if (child == mSwipeHelper.getTranslatingParentView()) {
2889 mSwipeHelper.clearTranslatingParentView();
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002890 }
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002891 }
2892
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002893 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002894 private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002895 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002896 // This is only a position change, don't do anything special
2897 return;
2898 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002899 child.setOnHeightChangedListener(null);
2900 updateScrollStateForRemovedChild(child);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002901 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002902 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002903 if (!mSwipedOutViews.contains(child)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002904 || Math.abs(child.getTranslation()) != child.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002905 container.addTransientView(child, 0);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002906 child.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002907 }
2908 } else {
2909 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002910 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002911 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002912
Selim Cineke9bad242016-06-15 11:46:37 -07002913 focusNextViewIfFocused(child);
2914 }
2915
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002916 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke9bad242016-06-15 11:46:37 -07002917 private void focusNextViewIfFocused(View view) {
2918 if (view instanceof ExpandableNotificationRow) {
2919 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2920 if (row.shouldRefocusOnDismiss()) {
2921 View nextView = row.getChildAfterViewWhenDismissed();
2922 if (nextView == null) {
2923 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2924 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2925 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002926 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002927 }
2928 if (nextView != null) {
2929 nextView.requestAccessibilityFocus();
2930 }
2931 }
2932 }
2933
Selim Cinekc27437b2014-05-14 10:23:33 +02002934 }
2935
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002936 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01002937 private boolean isChildInGroup(View child) {
2938 return child instanceof ExpandableNotificationRow
2939 && mGroupManager.isChildInGroupWithSummary(
Jason Monke59dc402018-08-16 12:05:01 -04002940 ((ExpandableNotificationRow) child).getStatusBarNotification());
Selim Cinekb5605e52015-02-20 18:21:41 +01002941 }
2942
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002943 /**
2944 * Generate a remove animation for a child view.
2945 *
2946 * @param child The view to generate the remove animation for.
2947 * @return Whether an animation was generated.
2948 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002949 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002950 private boolean generateRemoveAnimation(ExpandableView child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002951 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002952 mAddedHeadsUpChildren.remove(child);
2953 return false;
2954 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002955 if (isClickedHeadsUp(child)) {
Selim Cinek9dd0d042018-05-14 18:12:42 -07002956 // An animation is already running, add it transiently
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002957 mClearTransientViewsWhenFinished.add(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002958 return true;
2959 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002960 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002961 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002962 // Generate Animations
2963 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002964 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002965 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002966 } else {
2967 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002968 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002969 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002970 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002971 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002972 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002973 }
2974
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002975 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek0fccc722015-07-29 17:04:36 -07002976 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002977 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002978 }
2979
Selim Cineke0890e52015-06-17 11:17:08 -07002980 /**
2981 * Remove a removed child view from the heads up animations if it was just added there
2982 *
2983 * @return whether any child was removed from the list to animate
2984 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002985 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke0890e52015-06-17 11:17:08 -07002986 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2987 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002988 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2989 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002990 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002991 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002992 mTmpList.add(eventPair);
2993 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002994 }
2995 }
Selim Cineke0890e52015-06-17 11:17:08 -07002996 if (hasAddEvent) {
2997 // This child was just added lets remove all events.
2998 mHeadsUpChangeAnimations.removeAll(mTmpList);
Jason Monke59dc402018-08-16 12:05:01 -04002999 ((ExpandableNotificationRow) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07003000 }
3001 mTmpList.clear();
3002 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003003 }
3004
Selim Cinek572bbd42014-04-25 16:43:27 +02003005 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01003006 * @param child the child to query
3007 * @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 -04003008 * not expanded
Selim Cinekb5605e52015-02-20 18:21:41 +01003009 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003010 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003011 private boolean isChildInInvisibleGroup(View child) {
3012 if (child instanceof ExpandableNotificationRow) {
3013 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Ned Burnsf81c4c42019-01-07 14:10:43 -05003014 NotificationEntry groupSummary =
Selim Cinekb5605e52015-02-20 18:21:41 +01003015 mGroupManager.getGroupSummary(row.getStatusBarNotification());
Evan Laird94492852018-10-25 13:43:01 -04003016 if (groupSummary != null && groupSummary.getRow() != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07003017 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01003018 }
3019 }
3020 return false;
3021 }
3022
3023 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02003024 * Updates the scroll position when a child was removed
3025 *
3026 * @param removedChild the removed child
3027 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003028 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek61633a82016-01-25 15:54:10 -08003029 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003030 if (ANCHOR_SCROLLING) {
3031 if (removedChild == mScrollAnchorView) {
3032 ExpandableView firstChild = getFirstChildNotGone();
3033 if (firstChild != null) {
3034 mScrollAnchorView = firstChild;
3035 } else {
3036 mScrollAnchorView = mShelf;
3037 }
3038 // Adjust anchor view Y by the distance between the old and new anchors
3039 // so that there's no visible change.
3040 mScrollAnchorViewY +=
3041 mScrollAnchorView.getTranslationY() - removedChild.getTranslationY();
3042 }
3043 updateScrollAnchor();
3044 // TODO: once we're recycling this will need to check the adapter position of the child
3045 if (mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY > 0) {
3046 mScrollAnchorViewY = 0;
3047 }
3048 updateOnScrollChange();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003049 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003050 int startingPosition = getPositionInLinearLayout(removedChild);
3051 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
3052 int padding;
3053 if (increasedPaddingAmount >= 0) {
3054 padding = (int) NotificationUtils.interpolate(
3055 mPaddingBetweenElements,
3056 mIncreasedPaddingBetweenElements,
3057 increasedPaddingAmount);
3058 } else {
3059 padding = (int) NotificationUtils.interpolate(
3060 0,
3061 mPaddingBetweenElements,
3062 1.0f + increasedPaddingAmount);
3063 }
3064 int childHeight = getIntrinsicHeight(removedChild) + padding;
3065 int endPosition = startingPosition + childHeight;
3066 if (endPosition <= mOwnScrollY) {
3067 // This child is fully scrolled of the top, so we have to deduct its height from the
3068 // scrollPosition
3069 setOwnScrollY(mOwnScrollY - childHeight);
3070 } else if (startingPosition < mOwnScrollY) {
3071 // This child is currently being scrolled into, set the scroll position to the
3072 // start of this child
3073 setOwnScrollY(startingPosition);
3074 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003075 }
3076 }
3077
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003078 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd7c4e002014-07-04 18:36:42 +02003079 private int getIntrinsicHeight(View view) {
3080 if (view instanceof ExpandableView) {
3081 ExpandableView expandableView = (ExpandableView) view;
3082 return expandableView.getIntrinsicHeight();
3083 }
3084 return view.getHeight();
3085 }
3086
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003087 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1f624952017-06-08 19:11:50 -07003088 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07003089 ExpandableNotificationRow childInGroup = null;
3090 ExpandableNotificationRow requestedRow = null;
3091 if (isChildInGroup(requestedView)) {
3092 // We're asking for a child in a group. Calculate the position of the parent first,
3093 // then within the parent.
3094 childInGroup = (ExpandableNotificationRow) requestedView;
3095 requestedView = requestedRow = childInGroup.getNotificationParent();
3096 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003097 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08003098 float previousPaddingRequest = mPaddingBetweenElements;
3099 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02003100 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08003101 ExpandableView child = (ExpandableView) getChildAt(i);
3102 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07003103 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08003104 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003105 float padding;
3106 if (increasedPaddingAmount >= 0.0f) {
3107 padding = (int) NotificationUtils.interpolate(
3108 previousPaddingRequest,
3109 mIncreasedPaddingBetweenElements,
3110 increasedPaddingAmount);
3111 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08003112 mPaddingBetweenElements,
3113 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08003114 increasedPaddingAmount);
3115 } else {
3116 int ownPadding = (int) NotificationUtils.interpolate(
3117 0,
3118 mPaddingBetweenElements,
3119 1.0f + increasedPaddingAmount);
3120 if (previousPaddingAmount > 0.0f) {
3121 padding = (int) NotificationUtils.interpolate(
3122 ownPadding,
3123 mIncreasedPaddingBetweenElements,
3124 previousPaddingAmount);
3125 } else {
3126 padding = ownPadding;
3127 }
3128 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08003129 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08003130 if (position != 0) {
3131 position += padding;
3132 }
3133 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08003134 }
Adrian Roos4a579672016-05-24 16:54:37 -07003135 if (child == requestedView) {
3136 if (requestedRow != null) {
3137 position += requestedRow.getPositionOfChild(childInGroup);
3138 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003139 return position;
3140 }
Selim Cinek61633a82016-01-25 15:54:10 -08003141 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02003142 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003143 }
3144 }
3145 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02003146 }
3147
3148 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003149 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adam Powell6690d012015-06-17 16:41:56 -07003150 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02003151 super.onViewAdded(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003152 onViewAddedInternal((ExpandableView) child);
Selim Cinekb5605e52015-02-20 18:21:41 +01003153 }
3154
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003155 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08003156 private void updateFirstAndLastBackgroundViews() {
Gus Prevase2d6f042018-10-17 15:25:30 -04003157 NotificationSection firstSection = getFirstVisibleSection();
3158 NotificationSection lastSection = getLastVisibleSection();
Gus Prevasda13cfa2018-11-20 14:58:48 -05003159 ActivatableNotificationView previousFirstChild =
3160 firstSection == null ? null : firstSection.getFirstVisibleChild();
3161 ActivatableNotificationView previousLastChild =
3162 lastSection == null ? null : lastSection.getLastVisibleChild();
Gus Prevase2d6f042018-10-17 15:25:30 -04003163
Selim Cinek614576e2016-01-20 10:54:09 -08003164 ActivatableNotificationView firstChild = getFirstChildWithBackground();
3165 ActivatableNotificationView lastChild = getLastChildWithBackground();
Ned Burns9eb06332019-04-23 16:02:12 -04003166 boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsInSections(
Gus Prevase2d6f042018-10-17 15:25:30 -04003167 mSections[0], mSections[1], firstChild, lastChild);
3168
Selim Cinek614576e2016-01-20 10:54:09 -08003169 if (mAnimationsEnabled && mIsExpanded) {
Gus Prevasda13cfa2018-11-20 14:58:48 -05003170 mAnimateNextBackgroundTop = firstChild != previousFirstChild;
Selim Cinek6f0a62a2019-04-09 18:40:12 -07003171 mAnimateNextBackgroundBottom = lastChild != previousLastChild || mAnimateBottomOnLayout;
Gus Prevase2d6f042018-10-17 15:25:30 -04003172 mAnimateNextSectionBoundsChange = sectionViewsChanged;
Selim Cinek614576e2016-01-20 10:54:09 -08003173 } else {
3174 mAnimateNextBackgroundTop = false;
3175 mAnimateNextBackgroundBottom = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04003176 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08003177 }
Selim Cinekdb167372016-11-17 15:41:17 -08003178 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Gus Prevase2d6f042018-10-17 15:25:30 -04003179 mRoundnessManager.updateRoundedChildren(mSections);
Selim Cinek6f0a62a2019-04-09 18:40:12 -07003180 mAnimateBottomOnLayout = false;
Selim Cinek515b2032017-11-15 10:20:19 -08003181 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003182 }
3183
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003184 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003185 private void onViewAddedInternal(ExpandableView child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003186 updateHideSensitiveForChild(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003187 child.setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003188 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003189 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003190 updateChronometerForChild(child);
Gus Prevasa18dc572019-01-14 16:11:22 -05003191 if (child instanceof ExpandableNotificationRow) {
3192 ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
3193 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05003194 if (ANCHOR_SCROLLING) {
3195 // TODO: once we're recycling this will need to check the adapter position of the child
3196 if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) {
3197 // New child was added at the top while we're scrolled to the top;
3198 // make it the new anchor view so that we stay at the top.
3199 mScrollAnchorView = child;
3200 }
3201 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003202 }
3203
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003204 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003205 private void updateHideSensitiveForChild(ExpandableView child) {
3206 child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003207 }
3208
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003209 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003210 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003211 public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
Selim Cinekd1395642016-04-28 12:22:42 -07003212 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003213 }
3214
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003215 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003216 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003217 public void notifyGroupChildAdded(ExpandableView row) {
Selim Cinekb5605e52015-02-20 18:21:41 +01003218 onViewAddedInternal(row);
3219 }
3220
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003221 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003222 public void setAnimationsEnabled(boolean animationsEnabled) {
3223 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003224 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003225 if (!animationsEnabled) {
3226 mSwipedOutViews.clear();
3227 mChildrenToRemoveAnimated.clear();
3228 clearTemporaryViewsInGroup(this);
3229 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003230 }
3231
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003232 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003233 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003234 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003235 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003236 int childCount = getChildCount();
3237 for (int i = 0; i < childCount; i++) {
3238 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003239 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003240 updateAnimationState(running, child);
3241 }
3242 }
3243
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003244 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek51ae05d2014-09-09 15:51:38 +02003245 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003246 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003247 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003248 }
3249
Selim Cinek2627d722018-01-19 12:16:49 -08003250 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003251 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek2627d722018-01-19 12:16:49 -08003252 public void setExpandingNotification(ExpandableNotificationRow row) {
3253 mAmbientState.setExpandingNotification(row);
3254 requestChildrenUpdate();
3255 }
3256
3257 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003258 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek8875de12018-03-22 10:14:32 -07003259 public void bindRow(ExpandableNotificationRow row) {
Selim Cinekf0c79e12018-05-14 17:17:31 -07003260 row.setHeadsUpAnimatingAwayListener(animatingAway -> {
3261 mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
3262 mHeadsUpAppearanceController.updateHeader(row.getEntry());
3263 });
Selim Cinek8875de12018-03-22 10:14:32 -07003264 }
3265
3266 @Override
Selim Cinekfdf80332019-03-07 17:29:55 -08003267 public boolean containsView(View v) {
3268 return v.getParent() == this;
3269 }
3270
3271 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003272 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek2627d722018-01-19 12:16:49 -08003273 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3274 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3275 requestChildrenUpdate();
3276 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003277
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003278 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003279 private void updateAnimationState(boolean running, View child) {
3280 if (child instanceof ExpandableNotificationRow) {
3281 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3282 row.setIconAnimationRunning(running);
3283 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003284 }
3285
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003286 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003287 public boolean isAddOrRemoveAnimationPending() {
3288 return mNeedsAnimation
3289 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3290 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003291
3292 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003293 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003294 public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003295 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003296 // Generate Animations
3297 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003298 if (fromMoreCard) {
3299 mFromMoreCardAdditions.add(child);
3300 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003301 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003302 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003303 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003304 mAddedHeadsUpChildren.add(child);
3305 mChildrenToAddAnimated.remove(child);
3306 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003307 }
3308
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003309 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003310 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003311 public void changeViewPosition(ExpandableView child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003312 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003313
3314 if (currentIndex == -1) {
3315 boolean isTransient = false;
3316 if (child instanceof ExpandableNotificationRow
Jason Monke59dc402018-08-16 12:05:01 -04003317 && ((ExpandableNotificationRow) child).getTransientContainer() != null) {
Rohan Shah8ee53652018-04-05 11:13:50 -07003318 isTransient = true;
3319 }
3320 Log.e(TAG, "Attempting to re-position "
3321 + (isTransient ? "transient" : "")
3322 + " view {"
3323 + child
3324 + "}");
3325 return;
3326 }
3327
Dan Sandlereceda3d2014-07-21 15:35:01 -04003328 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003329 mChangePositionInProgress = true;
Jason Monke59dc402018-08-16 12:05:01 -04003330 ((ExpandableView) child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003331 removeView(child);
3332 addView(child, newIndex);
Jason Monke59dc402018-08-16 12:05:01 -04003333 ((ExpandableView) child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003334 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003335 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003336 mChildrenChangingPositions.add(child);
3337 mNeedsAnimation = true;
3338 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003339 }
3340 }
3341
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003342 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf4c19962014-05-01 21:55:31 +02003343 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003344 if (mNeedsAnimation) {
Rohan Shahb9d500a2018-06-25 16:27:16 -07003345 generateAllAnimationEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003346 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003347 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003348 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003349 setAnimationRunning(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003350 mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003351 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003352 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003353 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003354 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003355 } else {
3356 applyCurrentState();
3357 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003358 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003359 }
3360
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003361 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shahb9d500a2018-06-25 16:27:16 -07003362 private void generateAllAnimationEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003363 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003364 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003365 generateChildAdditionEvents();
3366 generatePositionChangeEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003367 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003368 generateActivateEvent();
3369 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003370 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003371 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003372 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003373 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003374 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003375 generateAnimateEverythingEvent();
Selim Cinek572bbd42014-04-25 16:43:27 +02003376 }
3377
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003378 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003379 private void generateHeadsUpAnimationEvents() {
3380 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003381 ExpandableNotificationRow row = eventPair.first;
3382 boolean isHeadsUp = eventPair.second;
3383 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3384 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003385 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003386 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003387 type = row.wasJustClicked()
3388 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3389 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003390 if (row.isChildInGroup()) {
3391 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003392 row.setHeadsUpAnimatingAway(false);
Selim Cinekf93bf3e2018-05-08 14:43:21 -07003393 continue;
Selim Cinek76e813c2016-07-14 11:16:58 -07003394 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003395 } else {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003396 ExpandableViewState viewState = row.getViewState();
Selim Cinekeaee9c02015-06-25 11:04:20 -04003397 if (viewState == null) {
3398 // A view state was never generated for this view, so we don't need to animate
3399 // this. This may happen with notification children.
3400 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003401 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003402 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3403 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3404 // Our custom add animation
3405 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3406 } else {
3407 // Normal add animation
3408 type = AnimationEvent.ANIMATION_TYPE_ADD;
3409 }
3410 onBottom = !pinnedAndClosed;
3411 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003412 }
3413 AnimationEvent event = new AnimationEvent(row, type);
3414 event.headsUpFromBottom = onBottom;
3415 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003416 }
3417 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003418 mAddedHeadsUpChildren.clear();
3419 }
3420
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003421 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbbcebde2016-11-09 18:28:20 -08003422 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003423 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3424 return false;
3425 }
3426 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003427 }
3428
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003429 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003430 private void generateGroupExpansionEvent() {
3431 // Generate a group expansion/collapsing event if there is such a group at all
3432 if (mExpandedGroupView != null) {
3433 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3434 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3435 mExpandedGroupView = null;
3436 }
3437 }
3438
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003439 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineka5e211b2014-08-11 17:35:48 +02003440 private void generateViewResizeEvent() {
3441 if (mNeedViewResizeAnimation) {
felkachangd7835b02018-07-17 18:18:13 +08003442 boolean hasDisappearAnimation = false;
3443 for (AnimationEvent animationEvent : mAnimationEvents) {
3444 final int type = animationEvent.animationType;
3445 if (type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
Jason Monke59dc402018-08-16 12:05:01 -04003446 || type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
felkachangd7835b02018-07-17 18:18:13 +08003447 hasDisappearAnimation = true;
3448 break;
3449 }
3450 }
3451
3452 if (!hasDisappearAnimation) {
3453 mAnimationEvents.add(
3454 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3455 }
Selim Cineka5e211b2014-08-11 17:35:48 +02003456 }
3457 mNeedViewResizeAnimation = false;
3458 }
3459
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003460 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003461 private void generateChildRemovalEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003462 for (ExpandableView child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003463 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003464
3465 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003466 float removedTranslation = child.getTranslationY();
3467 boolean ignoreChildren = true;
3468 if (child instanceof ExpandableNotificationRow) {
3469 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3470 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3471 removedTranslation = row.getTranslationWhenRemoved();
3472 ignoreChildren = false;
3473 }
Selim Cinek51052042017-07-04 12:07:55 +02003474 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003475 }
Selim Cinek51052042017-07-04 12:07:55 +02003476 if (!childWasSwipedOut) {
3477 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003478 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Rohan Shaha7594962018-05-22 10:59:30 -07003479
3480 if (childWasSwipedOut && child instanceof ExpandableView) {
3481 // Clean up any potential transient views if the child has already been swiped
3482 // out, as we won't be animating it further (due to its height already being
3483 // clipped to 0.
3484 ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
3485 if (transientContainer != null) {
3486 transientContainer.removeTransientView(child);
3487 }
3488 }
Selim Cinek51052042017-07-04 12:07:55 +02003489 }
3490 int animationType = childWasSwipedOut
3491 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3492 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3493 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003494 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3495 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003496 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003497 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003498 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003499 mChildrenToRemoveAnimated.clear();
3500 }
3501
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003502 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003503 private void generatePositionChangeEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003504 for (ExpandableView child : mChildrenChangingPositions) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003505 mAnimationEvents.add(new AnimationEvent(child,
3506 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3507 }
3508 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003509 if (mGenerateChildOrderChangedEvent) {
3510 mAnimationEvents.add(new AnimationEvent(null,
3511 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3512 mGenerateChildOrderChangedEvent = false;
3513 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003514 }
3515
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003516 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003517 private void generateChildAdditionEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003518 for (ExpandableView child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003519 if (mFromMoreCardAdditions.contains(child)) {
3520 mAnimationEvents.add(new AnimationEvent(child,
3521 AnimationEvent.ANIMATION_TYPE_ADD,
3522 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3523 } else {
3524 mAnimationEvents.add(new AnimationEvent(child,
3525 AnimationEvent.ANIMATION_TYPE_ADD));
3526 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003527 }
3528 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003529 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003530 }
3531
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003532 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003533 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003534 if (mTopPaddingNeedsAnimation) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003535 AnimationEvent event;
3536 if (mAmbientState.isDark()) {
3537 event = new AnimationEvent(null /* view */,
3538 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
3539 KeyguardSliceView.DEFAULT_ANIM_DURATION);
3540 } else {
3541 event = new AnimationEvent(null /* view */,
3542 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
3543 }
3544 mAnimationEvents.add(event);
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003545 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003546 mTopPaddingNeedsAnimation = false;
3547 }
3548
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003549 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003550 private void generateActivateEvent() {
3551 if (mActivateNeedsAnimation) {
3552 mAnimationEvents.add(
3553 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3554 }
3555 mActivateNeedsAnimation = false;
3556 }
3557
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003558 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd9acca52014-09-01 22:33:25 +02003559 private void generateAnimateEverythingEvent() {
3560 if (mEverythingNeedsAnimation) {
3561 mAnimationEvents.add(
3562 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3563 }
3564 mEverythingNeedsAnimation = false;
3565 }
3566
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003567 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003568 private void generateDimmedEvent() {
3569 if (mDimmedNeedsAnimation) {
3570 mAnimationEvents.add(
3571 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3572 }
3573 mDimmedNeedsAnimation = false;
3574 }
3575
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003576 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggiae441282014-08-01 02:45:18 +02003577 private void generateHideSensitiveEvent() {
3578 if (mHideSensitiveNeedsAnimation) {
3579 mAnimationEvents.add(
3580 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3581 }
3582 mHideSensitiveNeedsAnimation = false;
3583 }
3584
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003585 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
John Spurlockbf370992014-06-17 13:58:31 -04003586 private void generateDarkEvent() {
3587 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003588 AnimationEvent ev = new AnimationEvent(null,
3589 AnimationEvent.ANIMATION_TYPE_DARK,
3590 new AnimationFilter()
3591 .animateDark()
3592 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003593 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3594 mAnimationEvents.add(ev);
John Spurlockbf370992014-06-17 13:58:31 -04003595 }
3596 mDarkNeedsAnimation = false;
3597 }
3598
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003599 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003600 private void generateGoToFullShadeEvent() {
3601 if (mGoToFullShadeNeedsAnimation) {
3602 mAnimationEvents.add(
3603 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3604 }
3605 mGoToFullShadeNeedsAnimation = false;
3606 }
3607
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003608 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
3609 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003610 return new StackScrollAlgorithm(context, this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003611 }
3612
3613 /**
3614 * @return Whether a y coordinate is inside the content.
3615 */
3616 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
3617 public boolean isInContentBounds(float y) {
3618 return y < getHeight() - getEmptyBottomMargin();
3619 }
3620
3621 @ShadeViewRefactor(RefactorComponent.INPUT)
3622 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
3623 mLongPressListener = listener;
3624 }
3625
3626 @Override
3627 @ShadeViewRefactor(RefactorComponent.INPUT)
3628 public boolean onTouchEvent(MotionEvent ev) {
3629 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
3630 || ev.getActionMasked() == MotionEvent.ACTION_UP;
3631 handleEmptySpaceClick(ev);
3632 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003633 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003634 if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
3635 if (isCancelOrUp) {
3636 mExpandHelper.onlyObserveMovements(false);
3637 }
3638 boolean wasExpandingBefore = mExpandingNotification;
3639 expandWantsIt = mExpandHelper.onTouchEvent(ev);
3640 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
3641 && !mDisallowScrollingInThisMotion) {
3642 dispatchDownEventToScroller(ev);
3643 }
3644 }
3645 boolean scrollerWantsIt = false;
3646 if (mIsExpanded && !swipingInProgress && !mExpandingNotification
3647 && !mDisallowScrollingInThisMotion) {
3648 scrollerWantsIt = onScrollTouch(ev);
3649 }
3650 boolean horizontalSwipeWantsIt = false;
3651 if (!mIsBeingDragged
3652 && !mExpandingNotification
3653 && !mExpandedInThisMotion
3654 && !mOnlyScrollingInThisMotion
3655 && !mDisallowDismissInThisMotion) {
3656 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
3657 }
3658
3659 // Check if we need to clear any snooze leavebehinds
3660 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
3661 if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
3662 && guts.getGutsContent() instanceof NotificationSnooze) {
3663 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
3664 if ((ns.isExpanded() && isCancelOrUp)
3665 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
3666 // If the leavebehind is expanded we clear it on the next up event, otherwise we
3667 // clear it on the next non-horizontal swipe or expand event.
3668 checkSnoozeLeavebehind();
3669 }
3670 }
3671 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
3672 mCheckForLeavebehind = true;
3673 }
3674 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
3675 }
3676
3677 @ShadeViewRefactor(RefactorComponent.INPUT)
3678 private void dispatchDownEventToScroller(MotionEvent ev) {
3679 MotionEvent downEvent = MotionEvent.obtain(ev);
3680 downEvent.setAction(MotionEvent.ACTION_DOWN);
3681 onScrollTouch(downEvent);
3682 downEvent.recycle();
3683 }
3684
3685 @Override
3686 @ShadeViewRefactor(RefactorComponent.INPUT)
3687 public boolean onGenericMotionEvent(MotionEvent event) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003688 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003689 || mDisallowScrollingInThisMotion) {
3690 return false;
3691 }
3692 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
3693 switch (event.getAction()) {
3694 case MotionEvent.ACTION_SCROLL: {
3695 if (!mIsBeingDragged) {
3696 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3697 if (vscroll != 0) {
3698 final int delta = (int) (vscroll * getVerticalScrollFactor());
Gus Prevas0fa58d62019-01-11 13:58:40 -05003699 if (ANCHOR_SCROLLING) {
3700 mScrollAnchorViewY -= delta;
3701 updateScrollAnchor();
3702 clampScrollPosition();
3703 updateOnScrollChange();
3704 } else {
3705 final int range = getScrollRange();
3706 int oldScrollY = mOwnScrollY;
3707 int newScrollY = oldScrollY - delta;
3708 if (newScrollY < 0) {
3709 newScrollY = 0;
3710 } else if (newScrollY > range) {
3711 newScrollY = range;
3712 }
3713 if (newScrollY != oldScrollY) {
3714 setOwnScrollY(newScrollY);
3715 return true;
3716 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003717 }
3718 }
3719 }
3720 }
3721 }
3722 }
3723 return super.onGenericMotionEvent(event);
3724 }
3725
3726 @ShadeViewRefactor(RefactorComponent.INPUT)
3727 private boolean onScrollTouch(MotionEvent ev) {
3728 if (!isScrollingEnabled()) {
3729 return false;
3730 }
3731 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
3732 return false;
3733 }
3734 mForcedScroll = null;
3735 initVelocityTrackerIfNotExists();
3736 mVelocityTracker.addMovement(ev);
3737
3738 final int action = ev.getAction();
3739
3740 switch (action & MotionEvent.ACTION_MASK) {
3741 case MotionEvent.ACTION_DOWN: {
3742 if (getChildCount() == 0 || !isInContentBounds(ev)) {
3743 return false;
3744 }
3745 boolean isBeingDragged = !mScroller.isFinished();
3746 setIsBeingDragged(isBeingDragged);
3747 /*
3748 * If being flinged and user touches, stop the fling. isFinished
3749 * will be false if being flinged.
3750 */
3751 if (!mScroller.isFinished()) {
3752 mScroller.forceFinished(true);
3753 }
3754
3755 // Remember where the motion event started
3756 mLastMotionY = (int) ev.getY();
3757 mDownX = (int) ev.getX();
3758 mActivePointerId = ev.getPointerId(0);
3759 break;
3760 }
3761 case MotionEvent.ACTION_MOVE:
3762 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
3763 if (activePointerIndex == -1) {
3764 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
3765 break;
3766 }
3767
3768 final int y = (int) ev.getY(activePointerIndex);
3769 final int x = (int) ev.getX(activePointerIndex);
3770 int deltaY = mLastMotionY - y;
3771 final int xDiff = Math.abs(x - mDownX);
3772 final int yDiff = Math.abs(deltaY);
3773 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
3774 setIsBeingDragged(true);
3775 if (deltaY > 0) {
3776 deltaY -= mTouchSlop;
3777 } else {
3778 deltaY += mTouchSlop;
3779 }
3780 }
3781 if (mIsBeingDragged) {
3782 // Scroll to follow the motion event
3783 mLastMotionY = y;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003784 float scrollAmount;
Gus Prevas0fa58d62019-01-11 13:58:40 -05003785 int range;
3786 if (ANCHOR_SCROLLING) {
3787 range = 0; // unused in the methods it's being passed to
3788 } else {
3789 range = getScrollRange();
3790 if (mExpandedInThisMotion) {
3791 range = Math.min(range, mMaxScrollAfterExpand);
3792 }
3793 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003794 if (deltaY < 0) {
3795 scrollAmount = overScrollDown(deltaY);
3796 } else {
3797 scrollAmount = overScrollUp(deltaY, range);
3798 }
3799
3800 // Calling customOverScrollBy will call onCustomOverScrolled, which
3801 // sets the scrolling if applicable.
3802 if (scrollAmount != 0.0f) {
3803 // The scrolling motion could not be compensated with the
3804 // existing overScroll, we have to scroll the view
3805 customOverScrollBy((int) scrollAmount, mOwnScrollY,
3806 range, getHeight() / 2);
3807 // If we're scrolling, leavebehinds should be dismissed
3808 checkSnoozeLeavebehind();
3809 }
3810 }
3811 break;
3812 case MotionEvent.ACTION_UP:
3813 if (mIsBeingDragged) {
3814 final VelocityTracker velocityTracker = mVelocityTracker;
3815 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
3816 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
3817
3818 if (shouldOverScrollFling(initialVelocity)) {
3819 onOverScrollFling(true, initialVelocity);
3820 } else {
3821 if (getChildCount() > 0) {
3822 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
3823 float currentOverScrollTop = getCurrentOverScrollAmount(true);
3824 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
3825 fling(-initialVelocity);
3826 } else {
3827 onOverScrollFling(false, initialVelocity);
3828 }
3829 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003830 if (ANCHOR_SCROLLING) {
3831 // TODO
3832 } else {
3833 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3834 getScrollRange())) {
3835 animateScroll();
3836 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003837 }
3838 }
3839 }
3840 }
3841 mActivePointerId = INVALID_POINTER;
3842 endDrag();
3843 }
3844
3845 break;
3846 case MotionEvent.ACTION_CANCEL:
3847 if (mIsBeingDragged && getChildCount() > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003848 if (ANCHOR_SCROLLING) {
3849 // TODO
3850 } else {
3851 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3852 getScrollRange())) {
3853 animateScroll();
3854 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003855 }
3856 mActivePointerId = INVALID_POINTER;
3857 endDrag();
3858 }
3859 break;
3860 case MotionEvent.ACTION_POINTER_DOWN: {
3861 final int index = ev.getActionIndex();
3862 mLastMotionY = (int) ev.getY(index);
3863 mDownX = (int) ev.getX(index);
3864 mActivePointerId = ev.getPointerId(index);
3865 break;
3866 }
3867 case MotionEvent.ACTION_POINTER_UP:
3868 onSecondaryPointerUp(ev);
3869 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
3870 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
3871 break;
3872 }
3873 return true;
3874 }
3875
3876 @ShadeViewRefactor(RefactorComponent.INPUT)
3877 protected boolean isInsideQsContainer(MotionEvent ev) {
3878 return ev.getY() < mQsContainer.getBottom();
3879 }
3880
3881 @ShadeViewRefactor(RefactorComponent.INPUT)
3882 private void onOverScrollFling(boolean open, int initialVelocity) {
3883 if (mOverscrollTopChangedListener != null) {
3884 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
3885 }
3886 mDontReportNextOverScroll = true;
3887 setOverScrollAmount(0.0f, true, false);
3888 }
3889
3890
3891 @ShadeViewRefactor(RefactorComponent.INPUT)
3892 private void onSecondaryPointerUp(MotionEvent ev) {
3893 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
3894 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
3895 final int pointerId = ev.getPointerId(pointerIndex);
3896 if (pointerId == mActivePointerId) {
3897 // This was our active pointer going up. Choose a new
3898 // active pointer and adjust accordingly.
3899 // TODO: Make this decision more intelligent.
3900 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
3901 mLastMotionY = (int) ev.getY(newPointerIndex);
3902 mActivePointerId = ev.getPointerId(newPointerIndex);
3903 if (mVelocityTracker != null) {
3904 mVelocityTracker.clear();
3905 }
3906 }
3907 }
3908
3909 @ShadeViewRefactor(RefactorComponent.INPUT)
3910 private void endDrag() {
3911 setIsBeingDragged(false);
3912
3913 recycleVelocityTracker();
3914
3915 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
3916 setOverScrollAmount(0, true /* onTop */, true /* animate */);
3917 }
3918 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
3919 setOverScrollAmount(0, false /* onTop */, true /* animate */);
3920 }
3921 }
3922
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003923 @Override
3924 @ShadeViewRefactor(RefactorComponent.INPUT)
3925 public boolean onInterceptTouchEvent(MotionEvent ev) {
3926 initDownStates(ev);
3927 handleEmptySpaceClick(ev);
3928 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003929 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003930 if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
3931 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
3932 }
3933 boolean scrollWantsIt = false;
3934 if (!swipingInProgress && !mExpandingNotification) {
3935 scrollWantsIt = onInterceptTouchEventScroll(ev);
3936 }
3937 boolean swipeWantsIt = false;
3938 if (!mIsBeingDragged
3939 && !mExpandingNotification
3940 && !mExpandedInThisMotion
3941 && !mOnlyScrollingInThisMotion
3942 && !mDisallowDismissInThisMotion) {
3943 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
3944 }
3945 // Check if we need to clear any snooze leavebehinds
3946 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
3947 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
3948 if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
3949 !expandWantsIt && !scrollWantsIt) {
3950 mCheckForLeavebehind = false;
3951 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
3952 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3953 false /* resetMenu */);
3954 }
3955 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
3956 mCheckForLeavebehind = true;
3957 }
3958 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
3959 }
3960
3961 @ShadeViewRefactor(RefactorComponent.INPUT)
3962 private void handleEmptySpaceClick(MotionEvent ev) {
3963 switch (ev.getActionMasked()) {
3964 case MotionEvent.ACTION_MOVE:
3965 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
3966 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
3967 mTouchIsClick = false;
3968 }
3969 break;
3970 case MotionEvent.ACTION_UP:
3971 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
3972 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
3973 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
3974 }
3975 break;
3976 }
3977 }
3978
3979 @ShadeViewRefactor(RefactorComponent.INPUT)
3980 private void initDownStates(MotionEvent ev) {
3981 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
3982 mExpandedInThisMotion = false;
3983 mOnlyScrollingInThisMotion = !mScroller.isFinished();
3984 mDisallowScrollingInThisMotion = false;
3985 mDisallowDismissInThisMotion = false;
3986 mTouchIsClick = true;
3987 mInitialTouchX = ev.getX();
3988 mInitialTouchY = ev.getY();
3989 }
3990 }
3991
3992 @Override
3993 @ShadeViewRefactor(RefactorComponent.INPUT)
3994 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3995 super.requestDisallowInterceptTouchEvent(disallowIntercept);
3996 if (disallowIntercept) {
3997 cancelLongPress();
3998 }
3999 }
4000
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004001 @ShadeViewRefactor(RefactorComponent.INPUT)
Selim Cinek67b22602014-03-10 15:40:16 +01004002 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02004003 if (!isScrollingEnabled()) {
4004 return false;
4005 }
Selim Cinek67b22602014-03-10 15:40:16 +01004006 /*
4007 * This method JUST determines whether we want to intercept the motion.
4008 * If we return true, onMotionEvent will be called and we do the actual
4009 * scrolling there.
4010 */
4011
4012 /*
Jason Monke59dc402018-08-16 12:05:01 -04004013 * Shortcut the most recurring case: the user is in the dragging
4014 * state and is moving their finger. We want to intercept this
4015 * motion.
4016 */
Selim Cinek67b22602014-03-10 15:40:16 +01004017 final int action = ev.getAction();
4018 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
4019 return true;
4020 }
4021
Selim Cinek67b22602014-03-10 15:40:16 +01004022 switch (action & MotionEvent.ACTION_MASK) {
4023 case MotionEvent.ACTION_MOVE: {
4024 /*
4025 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04004026 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01004027 */
4028
4029 /*
Jason Monke59dc402018-08-16 12:05:01 -04004030 * Locally do absolute value. mLastMotionY is set to the y value
4031 * of the down event.
4032 */
Selim Cinek67b22602014-03-10 15:40:16 +01004033 final int activePointerId = mActivePointerId;
4034 if (activePointerId == INVALID_POINTER) {
4035 // If we don't have a valid id, the touch down wasn't on content.
4036 break;
4037 }
4038
4039 final int pointerIndex = ev.findPointerIndex(activePointerId);
4040 if (pointerIndex == -1) {
4041 Log.e(TAG, "Invalid pointerId=" + activePointerId
4042 + " in onInterceptTouchEvent");
4043 break;
4044 }
4045
4046 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02004047 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01004048 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02004049 final int xDiff = Math.abs(x - mDownX);
4050 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01004051 setIsBeingDragged(true);
4052 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004053 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01004054 initVelocityTrackerIfNotExists();
4055 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01004056 }
4057 break;
4058 }
4059
4060 case MotionEvent.ACTION_DOWN: {
4061 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05304062 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07004063 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01004064 setIsBeingDragged(false);
4065 recycleVelocityTracker();
4066 break;
4067 }
4068
4069 /*
4070 * Remember location of down touch.
4071 * ACTION_DOWN always refers to pointer index 0.
4072 */
4073 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004074 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01004075 mActivePointerId = ev.getPointerId(0);
4076
4077 initOrResetVelocityTracker();
4078 mVelocityTracker.addMovement(ev);
4079 /*
Jason Monke59dc402018-08-16 12:05:01 -04004080 * If being flinged and user touches the screen, initiate drag;
4081 * otherwise don't. mScroller.isFinished should be false when
4082 * being flinged.
4083 */
Selim Cinek67b22602014-03-10 15:40:16 +01004084 boolean isBeingDragged = !mScroller.isFinished();
4085 setIsBeingDragged(isBeingDragged);
4086 break;
4087 }
4088
4089 case MotionEvent.ACTION_CANCEL:
4090 case MotionEvent.ACTION_UP:
4091 /* Release the drag */
4092 setIsBeingDragged(false);
4093 mActivePointerId = INVALID_POINTER;
4094 recycleVelocityTracker();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004095 if (ANCHOR_SCROLLING) {
4096 // TODO
4097 } else {
4098 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
4099 animateScroll();
4100 }
Selim Cinek67b22602014-03-10 15:40:16 +01004101 }
4102 break;
4103 case MotionEvent.ACTION_POINTER_UP:
4104 onSecondaryPointerUp(ev);
4105 break;
4106 }
4107
4108 /*
Jason Monke59dc402018-08-16 12:05:01 -04004109 * The only time we want to intercept motion events is if we are in the
4110 * drag mode.
4111 */
Selim Cinek67b22602014-03-10 15:40:16 +01004112 return mIsBeingDragged;
4113 }
4114
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004115 /**
4116 * @return Whether the specified motion event is actually happening over the content.
4117 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004118 @ShadeViewRefactor(RefactorComponent.INPUT)
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004119 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01004120 return isInContentBounds(event.getY());
4121 }
4122
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004123
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004124 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004125 @ShadeViewRefactor(RefactorComponent.INPUT)
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004126 void setIsBeingDragged(boolean isDragged) {
Selim Cinek67b22602014-03-10 15:40:16 +01004127 mIsBeingDragged = isDragged;
4128 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004129 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004130 cancelLongPress();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004131 resetExposedMenuView(true /* animate */, true /* force */);
Selim Cinek67b22602014-03-10 15:40:16 +01004132 }
4133 }
4134
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004135 @ShadeViewRefactor(RefactorComponent.INPUT)
4136 public void requestDisallowLongPress() {
4137 cancelLongPress();
4138 }
4139
4140 @ShadeViewRefactor(RefactorComponent.INPUT)
4141 public void requestDisallowDismiss() {
4142 mDisallowDismissInThisMotion = true;
4143 }
4144
4145 @ShadeViewRefactor(RefactorComponent.INPUT)
4146 public void cancelLongPress() {
4147 mSwipeHelper.cancelLongPress();
4148 }
4149
4150 @ShadeViewRefactor(RefactorComponent.INPUT)
4151 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
4152 mOnEmptySpaceClickListener = listener;
4153 }
4154
4155 /** @hide */
4156 @Override
4157 @ShadeViewRefactor(RefactorComponent.INPUT)
4158 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4159 if (super.performAccessibilityActionInternal(action, arguments)) {
4160 return true;
4161 }
4162 if (!isEnabled()) {
4163 return false;
4164 }
4165 int direction = -1;
4166 switch (action) {
4167 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4168 // fall through
4169 case android.R.id.accessibilityActionScrollDown:
4170 direction = 1;
4171 // fall through
4172 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4173 // fall through
4174 case android.R.id.accessibilityActionScrollUp:
Gus Prevas0fa58d62019-01-11 13:58:40 -05004175 if (ANCHOR_SCROLLING) {
4176 // TODO
4177 } else {
4178 final int viewportHeight =
4179 getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
4180 - mShelf.getIntrinsicHeight();
4181 final int targetScrollY = Math.max(0,
4182 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4183 if (targetScrollY != mOwnScrollY) {
4184 mScroller.startScroll(mScrollX, mOwnScrollY, 0,
4185 targetScrollY - mOwnScrollY);
4186 animateScroll();
4187 return true;
4188 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004189 }
4190 break;
4191 }
4192 return false;
4193 }
4194
4195 @ShadeViewRefactor(RefactorComponent.INPUT)
4196 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4197 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
4198 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
4199 View translatingParentView = mSwipeHelper.getTranslatingParentView();
4200 View view = null;
4201 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4202 // Only close visible guts if they're not a leavebehind.
4203 view = guts;
4204 } else if (menuRow != null && menuRow.isMenuVisible()
4205 && translatingParentView != null) {
4206 // Checking menu
4207 view = translatingParentView;
4208 }
4209 if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
4210 // Touch was outside visible guts / menu notification, close what's visible
4211 mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
4212 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4213 false /* resetMenu */);
4214 resetExposedMenuView(true /* animate */, true /* force */);
4215 }
4216 }
4217
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05004218 @ShadeViewRefactor(RefactorComponent.INPUT)
4219 private void setSwipingInProgress(boolean swiping) {
4220 mSwipingInProgress = swiping;
4221 if (swiping) {
4222 requestDisallowInterceptTouchEvent(true);
4223 }
4224 }
4225
Selim Cinek67b22602014-03-10 15:40:16 +01004226 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004227 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01004228 public void onWindowFocusChanged(boolean hasWindowFocus) {
4229 super.onWindowFocusChanged(hasWindowFocus);
4230 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004231 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01004232 }
4233 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004234
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07004235 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004236 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04004237 public void clearChildFocus(View child) {
4238 super.clearChildFocus(child);
4239 if (mForcedScroll == child) {
4240 mForcedScroll = null;
4241 }
4242 }
4243
Selim Cinekfab078b2014-03-27 22:45:58 +01004244 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004245 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfab078b2014-03-27 22:45:58 +01004246 public boolean isScrolledToTop() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004247 if (ANCHOR_SCROLLING) {
4248 updateScrollAnchor();
4249 // TODO: once we're recycling this will need to check the adapter position of the child
4250 return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0;
4251 } else {
4252 return mOwnScrollY == 0;
4253 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004254 }
4255
4256 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004257 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004258 public boolean isScrolledToBottom() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004259 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05004260 return getMaxPositiveScrollAmount() <= 0;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004261 } else {
4262 return mOwnScrollY >= getScrollRange();
4263 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004264 }
4265
4266 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004267 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfab078b2014-03-27 22:45:58 +01004268 public View getHostView() {
4269 return this;
4270 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004271
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004272 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004273 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08004274 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004275 }
4276
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004277 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004278 public void checkSnoozeLeavebehind() {
4279 if (mCheckForLeavebehind) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04004280 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
Eliot Courtney47098cb2017-10-18 17:30:30 +09004281 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4282 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004283 mCheckForLeavebehind = false;
4284 }
4285 }
4286
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004287 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004288 public void resetCheckSnoozeLeavebehind() {
4289 mCheckForLeavebehind = true;
4290 }
4291
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004292 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004293 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004294 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004295 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004296 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02004297 }
4298
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004299 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004300 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004301 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004302 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08004303 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02004304 if (!mIsExpanded) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004305 resetScrollPosition();
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004306 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004307 clearTemporaryViews();
4308 clearUserLockedViews();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004309 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08004310 if (draggedViews.size() > 0) {
4311 draggedViews.clear();
4312 updateContinuousShadowDrawing();
4313 }
Selim Cinek5b1591a2017-07-03 17:05:01 +02004314 }
4315 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01004316
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004317 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004318 private void clearUserLockedViews() {
4319 for (int i = 0; i < getChildCount(); i++) {
4320 ExpandableView child = (ExpandableView) getChildAt(i);
4321 if (child instanceof ExpandableNotificationRow) {
4322 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4323 row.setUserLocked(false);
4324 }
4325 }
4326 }
4327
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004328 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004329 private void clearTemporaryViews() {
Selim Cinek9dd0d042018-05-14 18:12:42 -07004330 // lets make sure nothing is transient anymore
Rohan Shah8ee53652018-04-05 11:13:50 -07004331 clearTemporaryViewsInGroup(this);
Selim Cinek5b1591a2017-07-03 17:05:01 +02004332 for (int i = 0; i < getChildCount(); i++) {
4333 ExpandableView child = (ExpandableView) getChildAt(i);
4334 if (child instanceof ExpandableNotificationRow) {
4335 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Rohan Shah8ee53652018-04-05 11:13:50 -07004336 clearTemporaryViewsInGroup(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07004337 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02004338 }
Selim Cinek1685e632014-04-08 02:27:49 +02004339 }
4340
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004341 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shah8ee53652018-04-05 11:13:50 -07004342 private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07004343 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04004344 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07004345 }
4346 }
4347
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004348 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004349 public void onPanelTrackingStarted() {
4350 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004351 mAmbientState.setPanelTracking(true);
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004352 resetExposedMenuView(true /* animate */, true /* force */);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004353 }
Jason Monke59dc402018-08-16 12:05:01 -04004354
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004355 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004356 public void onPanelTrackingStopped() {
4357 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004358 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004359 }
4360
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004361 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb24e0a92015-06-09 20:17:30 -07004362 public void resetScrollPosition() {
4363 mScroller.abortAnimation();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004364 if (ANCHOR_SCROLLING) {
4365 // TODO: once we're recycling this will need to modify the adapter position instead
4366 mScrollAnchorView = getFirstChildNotGone();
4367 mScrollAnchorViewY = 0;
4368 updateOnScrollChange();
4369 } else {
4370 setOwnScrollY(0);
4371 }
Selim Cinekb24e0a92015-06-09 20:17:30 -07004372 }
4373
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004374 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek3d6ae232019-01-04 14:14:33 -08004375 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02004376 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02004377 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02004378 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinek3d6ae232019-01-04 14:14:33 -08004379 mAmbientState.setShadeExpanded(isExpanded);
4380 mStateAnimator.setShadeExpanded(isExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02004381 if (changed) {
Selim Cinek9184f9c2016-02-02 17:36:53 -08004382 if (!mIsExpanded) {
4383 mGroupManager.collapseAllGroups();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004384 mExpandHelper.cancelImmediately();
Selim Cinek9184f9c2016-02-02 17:36:53 -08004385 }
Selim Cinekcab4a602014-09-03 14:47:57 +02004386 updateNotificationAnimationStates();
Selim Cinek98713a42015-09-21 15:47:20 +02004387 updateChronometers();
Selim Cinekf3fa6852016-12-20 18:36:02 +01004388 requestChildrenUpdate();
Selim Cinek98713a42015-09-21 15:47:20 +02004389 }
4390 }
4391
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004392 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek98713a42015-09-21 15:47:20 +02004393 private void updateChronometers() {
4394 int childCount = getChildCount();
4395 for (int i = 0; i < childCount; i++) {
4396 updateChronometerForChild(getChildAt(i));
4397 }
4398 }
4399
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004400 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek98713a42015-09-21 15:47:20 +02004401 private void updateChronometerForChild(View child) {
4402 if (child instanceof ExpandableNotificationRow) {
4403 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4404 row.setChronometerRunning(mIsExpanded);
Selim Cinekcab4a602014-09-03 14:47:57 +02004405 }
Selim Cinek1685e632014-04-08 02:27:49 +02004406 }
4407
Jorim Jaggibe565df2014-04-28 17:51:23 +02004408 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01004409 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004410 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02004411 updateScrollPositionOnExpandInBottom(view);
4412 clampScrollPosition();
Lucas Dupin60661a62018-04-12 10:50:13 -07004413 notifyHeightChangeListener(view, needsAnimation);
Selim Cinekbc243a92016-09-27 16:35:13 -07004414 ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
4415 ? (ExpandableNotificationRow) view
4416 : null;
Gus Prevase2d6f042018-10-17 15:25:30 -04004417 NotificationSection firstSection = getFirstVisibleSection();
4418 ActivatableNotificationView firstVisibleChild =
4419 firstSection == null ? null : firstSection.getFirstVisibleChild();
4420 if (row != null) {
4421 if (row == firstVisibleChild
4422 || row.getNotificationParent() == firstVisibleChild) {
4423 updateAlgorithmLayoutMinHeight();
4424 }
Selim Cinekbc243a92016-09-27 16:35:13 -07004425 }
Selim Cinekb5605e52015-02-20 18:21:41 +01004426 if (needsAnimation) {
Selim Cinek5bc852a2015-12-21 12:19:09 -08004427 requestAnimationOnViewResize(row);
Selim Cinekb5605e52015-02-20 18:21:41 +01004428 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004429 requestChildrenUpdate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02004430 }
4431
Selim Cineka5e211b2014-08-11 17:35:48 +02004432 @Override
4433 public void onReset(ExpandableView view) {
Selim Cinek51ae05d2014-09-09 15:51:38 +02004434 updateAnimationState(view);
Selim Cinek98713a42015-09-21 15:47:20 +02004435 updateChronometerForChild(view);
Selim Cineka5e211b2014-08-11 17:35:48 +02004436 }
4437
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004438 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf7a14c02014-07-07 14:01:46 +02004439 private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
Selim Cinek51d21972017-07-19 17:39:20 -07004440 if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
Selim Cinekf7a14c02014-07-07 14:01:46 +02004441 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004442 // TODO: once we're recycling this will need to check the adapter position of the child
Selim Cinekcb9400a2015-06-03 16:56:13 +02004443 if (row.isUserLocked() && row != getFirstChildNotGone()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08004444 if (row.isSummaryWithChildren()) {
4445 return;
4446 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02004447 // We are actually expanding this view
Selim Cinek4e4cac32016-03-11 16:45:52 -08004448 float endPosition = row.getTranslationY() + row.getActualHeight();
Selim Cinek388df6d2015-10-22 13:25:11 -07004449 if (row.isChildInGroup()) {
Selim Cinek4e4cac32016-03-11 16:45:52 -08004450 endPosition += row.getNotificationParent().getTranslationY();
Selim Cinek388df6d2015-10-22 13:25:11 -07004451 }
Selim Cinekdb167372016-11-17 15:41:17 -08004452 int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
Gus Prevase2d6f042018-10-17 15:25:30 -04004453 NotificationSection lastSection = getLastVisibleSection();
4454 ActivatableNotificationView lastVisibleChild =
4455 lastSection == null ? null : lastSection.getLastVisibleChild();
4456 if (row != lastVisibleChild && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08004457 layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
4458 }
4459 if (endPosition > layoutEnd) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004460 if (ANCHOR_SCROLLING) {
4461 mScrollAnchorViewY -= (endPosition - layoutEnd);
4462 updateScrollAnchor();
4463 updateOnScrollChange();
4464 } else {
4465 setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
4466 }
Selim Cinekf7a14c02014-07-07 14:01:46 +02004467 mDisallowScrollingInThisMotion = true;
4468 }
4469 }
4470 }
4471 }
4472
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004473 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggibe565df2014-04-28 17:51:23 +02004474 public void setOnHeightChangedListener(
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004475 ExpandableView.OnHeightChangedListener onHeightChangedListener) {
4476 this.mOnHeightChangedListener = onHeightChangedListener;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01004477 }
4478
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004479 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02004480 public void onChildAnimationFinished() {
Selim Cinek6811d722016-01-19 17:53:12 -08004481 setAnimationRunning(false);
Selim Cinek319bdc42014-05-01 23:01:58 +02004482 requestChildrenUpdate();
Selim Cinek32a59fd32015-06-10 13:54:42 -07004483 runAnimationFinishedRunnables();
Selim Cinek9dd0d042018-05-14 18:12:42 -07004484 clearTransient();
Selim Cinek8fc78752016-07-13 14:34:56 -07004485 clearHeadsUpDisappearRunning();
4486 }
4487
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004488 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8fc78752016-07-13 14:34:56 -07004489 private void clearHeadsUpDisappearRunning() {
4490 for (int i = 0; i < getChildCount(); i++) {
4491 View view = getChildAt(i);
4492 if (view instanceof ExpandableNotificationRow) {
Selim Cinek76e813c2016-07-14 11:16:58 -07004493 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekcafa87f2016-10-26 17:00:17 -07004494 row.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07004495 if (row.isSummaryWithChildren()) {
4496 for (ExpandableNotificationRow child : row.getNotificationChildren()) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07004497 child.setHeadsUpAnimatingAway(false);
Selim Cinek76e813c2016-07-14 11:16:58 -07004498 }
4499 }
Selim Cinek8fc78752016-07-13 14:34:56 -07004500 }
4501 }
Selim Cinek0fccc722015-07-29 17:04:36 -07004502 }
4503
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004504 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9dd0d042018-05-14 18:12:42 -07004505 private void clearTransient() {
4506 for (ExpandableView view : mClearTransientViewsWhenFinished) {
4507 StackStateAnimator.removeTransientView(view);
Selim Cinek0fccc722015-07-29 17:04:36 -07004508 }
Selim Cinek9dd0d042018-05-14 18:12:42 -07004509 mClearTransientViewsWhenFinished.clear();
Selim Cinek32a59fd32015-06-10 13:54:42 -07004510 }
4511
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004512 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek32a59fd32015-06-10 13:54:42 -07004513 private void runAnimationFinishedRunnables() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07004514 for (Runnable runnable : mAnimationFinishedRunnables) {
4515 runnable.run();
4516 }
4517 mAnimationFinishedRunnables.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02004518 }
4519
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004520 /**
4521 * See {@link AmbientState#setDimmed}.
4522 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004523 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004524 public void setDimmed(boolean dimmed, boolean animate) {
Selim Cinek8a9308d2017-08-24 09:31:08 -07004525 dimmed &= onKeyguard();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004526 mAmbientState.setDimmed(dimmed);
Jorim Jaggi75c95042014-05-16 19:09:59 +02004527 if (animate && mAnimationsEnabled) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004528 mDimmedNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004529 mNeedsAnimation = true;
Selim Cinekd35c2792016-01-21 13:20:57 -08004530 animateDimmed(dimmed);
4531 } else {
4532 setDimAmount(dimmed ? 1.0f : 0.0f);
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004533 }
4534 requestChildrenUpdate();
4535 }
4536
Selim Cinek8a9308d2017-08-24 09:31:08 -07004537 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004538 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8a9308d2017-08-24 09:31:08 -07004539 boolean isDimmed() {
4540 return mAmbientState.isDimmed();
4541 }
4542
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004543 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd35c2792016-01-21 13:20:57 -08004544 private void setDimAmount(float dimAmount) {
4545 mDimAmount = dimAmount;
4546 updateBackgroundDimming();
4547 }
4548
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004549 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd35c2792016-01-21 13:20:57 -08004550 private void animateDimmed(boolean dimmed) {
4551 if (mDimAnimator != null) {
4552 mDimAnimator.cancel();
4553 }
4554 float target = dimmed ? 1.0f : 0.0f;
4555 if (target == mDimAmount) {
4556 return;
4557 }
4558 mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
4559 mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
4560 mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
4561 mDimAnimator.addListener(mDimEndListener);
4562 mDimAnimator.addUpdateListener(mDimUpdateListener);
4563 mDimAnimator.start();
4564 }
Evan Laird91d0f102018-09-18 17:39:55 -04004565
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004566 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Evan Laird91d0f102018-09-18 17:39:55 -04004567 private void setHideSensitive(boolean hideSensitive, boolean animate) {
Jorim Jaggiae441282014-08-01 02:45:18 +02004568 if (hideSensitive != mAmbientState.isHideSensitive()) {
4569 int childCount = getChildCount();
4570 for (int i = 0; i < childCount; i++) {
4571 ExpandableView v = (ExpandableView) getChildAt(i);
4572 v.setHideSensitiveForIntrinsicHeight(hideSensitive);
4573 }
4574 mAmbientState.setHideSensitive(hideSensitive);
4575 if (animate && mAnimationsEnabled) {
4576 mHideSensitiveNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004577 mNeedsAnimation = true;
Jorim Jaggiae441282014-08-01 02:45:18 +02004578 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08004579 updateContentHeight();
Jorim Jaggiae441282014-08-01 02:45:18 +02004580 requestChildrenUpdate();
4581 }
4582 }
4583
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004584 /**
4585 * See {@link AmbientState#setActivatedChild}.
4586 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004587 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka32ab602014-06-11 15:06:01 +02004588 public void setActivatedChild(ActivatableNotificationView activatedChild) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004589 mAmbientState.setActivatedChild(activatedChild);
Jorim Jaggi75c95042014-05-16 19:09:59 +02004590 if (mAnimationsEnabled) {
4591 mActivateNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04004592 mNeedsAnimation = true;
Jorim Jaggi75c95042014-05-16 19:09:59 +02004593 }
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004594 requestChildrenUpdate();
4595 }
4596
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004597 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka32ab602014-06-11 15:06:01 +02004598 public ActivatableNotificationView getActivatedChild() {
Jorim Jaggid552d9d2014-05-07 19:41:13 +02004599 return mAmbientState.getActivatedChild();
4600 }
4601
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004602 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02004603 private void applyCurrentState() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004604 int numChildren = getChildCount();
4605 for (int i = 0; i < numChildren; i++) {
4606 ExpandableView child = (ExpandableView) getChildAt(i);
4607 child.applyViewState();
4608 }
4609
Selim Cinekf4c19962014-05-01 21:55:31 +02004610 if (mListener != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09004611 mListener.onChildLocationsChanged();
Selim Cinekf4c19962014-05-01 21:55:31 +02004612 }
Selim Cinek32a59fd32015-06-10 13:54:42 -07004613 runAnimationFinishedRunnables();
Selim Cinekea66ca02016-05-24 13:33:47 -07004614 setAnimationRunning(false);
Selim Cinek6811d722016-01-19 17:53:12 -08004615 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08004616 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08004617 updateClippingToTopRoundedCorner();
Selim Cinek33223572016-02-19 19:32:22 -08004618 }
4619
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004620 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08004621 private void updateViewShadows() {
4622 // we need to work around an issue where the shadow would not cast between siblings when
4623 // their z difference is between 0 and 0.1
4624
4625 // Lefts first sort by Z difference
4626 for (int i = 0; i < getChildCount(); i++) {
4627 ExpandableView child = (ExpandableView) getChildAt(i);
4628 if (child.getVisibility() != GONE) {
4629 mTmpSortedChildren.add(child);
4630 }
4631 }
4632 Collections.sort(mTmpSortedChildren, mViewPositionComparator);
4633
4634 // Now lets update the shadow for the views
4635 ExpandableView previous = null;
4636 for (int i = 0; i < mTmpSortedChildren.size(); i++) {
4637 ExpandableView expandableView = mTmpSortedChildren.get(i);
4638 float translationZ = expandableView.getTranslationZ();
4639 float otherZ = previous == null ? translationZ : previous.getTranslationZ();
4640 float diff = otherZ - translationZ;
4641 if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
4642 // There is no fake shadow to be drawn
4643 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
4644 } else {
4645 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
Mady Mellorb0a82462016-04-30 17:31:02 -07004646 expandableView.getTranslationY() - previous.getExtraBottomPadding();
4647 expandableView.setFakeShadowIntensity(
4648 diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
Selim Cinek33223572016-02-19 19:32:22 -08004649 previous.getOutlineAlpha(), (int) yLocation,
4650 previous.getOutlineTranslation());
4651 }
4652 previous = expandableView;
4653 }
4654
4655 mTmpSortedChildren.clear();
Selim Cinek572bbd42014-04-25 16:43:27 +02004656 }
4657
Lucas Dupine17ce522017-07-17 15:45:06 -07004658 /**
4659 * Update colors of "dismiss" and "empty shade" views.
4660 *
4661 * @param lightTheme True if light theme should be used.
4662 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004663 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupine17ce522017-07-17 15:45:06 -07004664 public void updateDecorViews(boolean lightTheme) {
4665 if (lightTheme == mUsingLightTheme) {
4666 return;
4667 }
4668 mUsingLightTheme = lightTheme;
4669 Context context = new ContextThemeWrapper(mContext,
4670 lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
Jason Changb4e879d2018-04-11 11:17:58 +08004671 final int textColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor);
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004672 mFooterView.setTextColor(textColor);
Lucas Dupine17ce522017-07-17 15:45:06 -07004673 mEmptyShadeView.setTextColor(textColor);
4674 }
4675
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004676 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004677 public void goToFullShade(long delay) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004678 mGoToFullShadeNeedsAnimation = true;
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02004679 mGoToFullShadeDelay = delay;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004680 mNeedsAnimation = true;
Jorim Jaggi60d07c52014-07-31 15:38:21 +02004681 requestChildrenUpdate();
Selim Cinekc27437b2014-05-14 10:23:33 +02004682 }
4683
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004684 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02004685 public void cancelExpandHelper() {
4686 mExpandHelper.cancel();
4687 }
4688
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004689 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02004690 public void setIntrinsicPadding(int intrinsicPadding) {
4691 mIntrinsicPadding = intrinsicPadding;
Selim Cinek1f624952017-06-08 19:11:50 -07004692 mAmbientState.setIntrinsicPadding(intrinsicPadding);
Selim Cinek1408eb52014-06-02 14:45:38 +02004693 }
4694
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004695 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02004696 public int getIntrinsicPadding() {
4697 return mIntrinsicPadding;
4698 }
4699
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004700 /**
Jorim Jaggi457cc352014-06-02 22:47:42 +02004701 * @return the y position of the first notification
4702 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004703 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi457cc352014-06-02 22:47:42 +02004704 public float getNotificationsTopY() {
Selim Cinekd2281152015-04-10 14:37:46 -07004705 return mTopPadding + getStackTranslation();
Jorim Jaggi457cc352014-06-02 22:47:42 +02004706 }
4707
Selim Cinekc0ce82d2014-06-10 13:21:15 +02004708 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004709 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc0ce82d2014-06-10 13:21:15 +02004710 public boolean shouldDelayChildPressedState() {
4711 return true;
4712 }
4713
Jorim Jaggi457cc352014-06-02 22:47:42 +02004714 /**
John Spurlockbf370992014-06-17 13:58:31 -04004715 * See {@link AmbientState#setDark}.
4716 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004717 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004718 public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
Adrian Roos260c1f72017-08-07 15:52:26 +02004719 if (mAmbientState.isDark() == dark) {
4720 return;
4721 }
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004722 if (!dark) {
4723 mShowDarkShelf = false;
4724 }
John Spurlockbf370992014-06-17 13:58:31 -04004725 mAmbientState.setDark(dark);
4726 if (animate && mAnimationsEnabled) {
4727 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004728 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004729 mNeedsAnimation = true;
Selim Cinek6811d722016-01-19 17:53:12 -08004730 } else {
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004731 setDarkAmount(dark ? 1f : 0f);
Selim Cinek6811d722016-01-19 17:53:12 -08004732 updateBackground();
Selim Cinek6811d722016-01-19 17:53:12 -08004733 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004734 requestChildrenUpdate();
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004735 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004736 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04004737 }
4738
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004739 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Bill Line60aa1e2018-06-13 18:07:15 +08004740 private void updatePanelTranslation() {
Lucas Dupinb46d0a22019-01-11 16:57:16 -08004741 setTranslationX(mHorizontalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
Bill Line60aa1e2018-06-13 18:07:15 +08004742 }
4743
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004744 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupinb46d0a22019-01-11 16:57:16 -08004745 public void setHorizontalPanelTranslation(float verticalPanelTranslation) {
4746 mHorizontalPanelTranslation = verticalPanelTranslation;
Bill Line60aa1e2018-06-13 18:07:15 +08004747 updatePanelTranslation();
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004748 }
4749
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004750 /**
4751 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
4752 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
4753 * {@link #mAmbientState}'s dark mode is toggled.
4754 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004755 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004756 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08004757 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07004758 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004759 }
4760
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004761 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupind285cf02018-01-18 09:18:23 -08004762 private void setDarkAmount(float darkAmount) {
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004763 setDarkAmount(darkAmount, darkAmount);
4764 }
4765
4766 /**
4767 * Sets the current dark amount.
4768 *
Jason Monke59dc402018-08-16 12:05:01 -04004769 * @param linearDarkAmount The dark amount that follows linear interpoloation in the
4770 * animation,
4771 * i.e. animates from 0 to 1 or vice-versa in a linear manner.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004772 * @param interpolatedDarkAmount The dark amount that follows the actual interpolation of the
Jason Monke59dc402018-08-16 12:05:01 -04004773 * animation curve.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004774 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004775 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004776 public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
4777 mLinearDarkAmount = linearDarkAmount;
4778 mInterpolatedDarkAmount = interpolatedDarkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -07004779 boolean wasFullyDark = mAmbientState.isFullyDark();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004780 boolean wasDarkAtAll = mAmbientState.isDarkAtAll();
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004781 mAmbientState.setDarkAmount(interpolatedDarkAmount);
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004782 boolean nowFullyDark = mAmbientState.isFullyDark();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004783 boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004784 if (nowFullyDark != wasFullyDark) {
Lucas Dupin16cfe452018-02-08 13:14:50 -08004785 updateContentHeight();
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004786 if (nowFullyDark && mShowDarkShelf) {
4787 updateDarkShelfVisibility();
4788 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08004789 }
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004790 if (!wasDarkAtAll && nowDarkAtAll) {
4791 resetExposedMenuView(true /* animate */, true /* animate */);
4792 }
Lucas Dupin64e2f572019-03-21 14:21:14 -07004793 if (nowFullyDark != wasFullyDark || wasDarkAtAll != nowDarkAtAll) {
4794 invalidateOutline();
4795 }
Lucas Dupin60661a62018-04-12 10:50:13 -07004796 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07004797 updateBackgroundDimming();
Bill Line60aa1e2018-06-13 18:07:15 +08004798 updatePanelTranslation();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004799 requestChildrenUpdate();
Selim Cinek972123d2016-05-03 14:25:58 -07004800 }
4801
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004802 /**
4803 * If the shelf should be visible when the device is in ambient mode (dozing.)
4804 */
4805 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Robert Snoeberger69956802019-04-16 16:55:21 -04004806 public void showDarkShelf() {
4807 mShowDarkShelf = true;
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004808 }
4809
4810 private void updateDarkShelfVisibility() {
4811 DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
4812 if (dozeParameters.shouldControlScreenOff()) {
4813 mShelf.fadeInTranslating();
4814 }
4815 updateClipping();
4816 }
4817
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004818 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004819 public void notifyDarkAnimationStart(boolean dark) {
4820 // We only swap the scaling factor if we're fully dark or fully awake to avoid
4821 // interpolation issues when playing with the power button.
4822 if (mInterpolatedDarkAmount == 0 || mInterpolatedDarkAmount == 1) {
4823 mBackgroundXFactor = dark ? 1.8f : 1.5f;
4824 mDarkXInterpolator = dark
4825 ? Interpolators.FAST_OUT_SLOW_IN_REVERSE
4826 : Interpolators.FAST_OUT_SLOW_IN;
4827 }
Selim Cinek972123d2016-05-03 14:25:58 -07004828 }
4829
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004830 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004831 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07004832 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004833 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4834 }
4835 if (screenLocation.y > getBottomMostNotificationBottom()) {
4836 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
4837 }
4838 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
4839 if (child != null) {
4840 return getNotGoneIndex(child);
4841 } else {
4842 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4843 }
4844 }
4845
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004846 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004847 private int getNotGoneIndex(View child) {
4848 int count = getChildCount();
4849 int notGoneIndex = 0;
4850 for (int i = 0; i < count; i++) {
4851 View v = getChildAt(i);
4852 if (child == v) {
4853 return notGoneIndex;
4854 }
4855 if (v.getVisibility() != View.GONE) {
4856 notGoneIndex++;
4857 }
4858 }
4859 return -1;
4860 }
4861
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004862 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004863 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004864 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004865 if (mFooterView != null) {
4866 index = indexOfChild(mFooterView);
4867 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004868 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004869 mFooterView = footerView;
4870 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004871 }
4872
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004873 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004874 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004875 int index = -1;
4876 if (mEmptyShadeView != null) {
4877 index = indexOfChild(mEmptyShadeView);
4878 removeView(mEmptyShadeView);
4879 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004880 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004881 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004882 }
4883
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004884 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004885 public void updateEmptyShadeView(boolean visible) {
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004886 mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
Lucas Dupinc9274ff2018-05-09 17:40:20 -07004887
4888 int oldTextRes = mEmptyShadeView.getTextResource();
4889 int newTextRes = mStatusBar.areNotificationsHidden()
4890 ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
4891 if (oldTextRes != newTextRes) {
4892 mEmptyShadeView.setText(newTextRes);
4893 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004894 }
4895
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004896 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004897 public void updateFooterView(boolean visible, boolean showDismissView) {
4898 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004899 return;
4900 }
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004901 boolean animate = mIsExpanded && mAnimationsEnabled;
4902 mFooterView.setVisible(visible, animate);
4903 mFooterView.setSecondaryVisible(showDismissView, animate);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004904 }
4905
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004906 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dan Sandlereceda3d2014-07-21 15:35:01 -04004907 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4908 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004909 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004910 handleDismissAllClipping();
4911 }
4912
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004913 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek9c17b772015-07-07 20:37:09 -07004914 private void handleDismissAllClipping() {
4915 final int count = getChildCount();
4916 boolean previousChildWillBeDismissed = false;
4917 for (int i = 0; i < count; i++) {
4918 ExpandableView child = (ExpandableView) getChildAt(i);
4919 if (child.getVisibility() == GONE) {
4920 continue;
4921 }
4922 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4923 child.setMinClipTopAmount(child.getClipTopAmount());
4924 } else {
4925 child.setMinClipTopAmount(0);
4926 }
Ned Burns61269442019-05-02 18:27:23 -04004927 previousChildWillBeDismissed = StackScrollAlgorithm.canChildBeDismissed(child);
Selim Cinek9c17b772015-07-07 20:37:09 -07004928 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004929 }
4930
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004931 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004932 public boolean isFooterViewNotGone() {
4933 return mFooterView != null
4934 && mFooterView.getVisibility() != View.GONE
4935 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004936 }
4937
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004938 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek38475442018-05-18 11:11:46 -07004939 public boolean isFooterViewContentVisible() {
4940 return mFooterView != null && mFooterView.isContentVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004941 }
4942
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004943 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004944 public int getFooterViewHeight() {
4945 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004946 }
4947
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004948 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004949 public int getEmptyShadeViewHeight() {
4950 return mEmptyShadeView.getHeight();
4951 }
4952
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004953 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004954 public float getBottomMostNotificationBottom() {
4955 final int count = getChildCount();
4956 float max = 0;
4957 for (int childIdx = 0; childIdx < count; childIdx++) {
4958 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4959 if (child.getVisibility() == GONE) {
4960 continue;
4961 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004962 float bottom = child.getTranslationY() + child.getActualHeight()
4963 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004964 if (bottom > max) {
4965 max = bottom;
4966 }
4967 }
Selim Cinekd2281152015-04-10 14:37:46 -07004968 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004969 }
4970
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004971 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004972 public void setStatusBar(StatusBar statusBar) {
4973 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004974 }
4975
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004976 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb5605e52015-02-20 18:21:41 +01004977 public void setGroupManager(NotificationGroupManager groupManager) {
4978 this.mGroupManager = groupManager;
Kevin01a53cb2018-11-09 18:19:54 -08004979 mGroupManager.addOnGroupChangeListener(mOnGroupChangeListener);
Selim Cinek379ff8f2015-02-20 17:03:16 +01004980 }
4981
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004982 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek379ff8f2015-02-20 17:03:16 +01004983 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004984 if (mIsExpanded && mAnimationsEnabled) {
4985 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004986 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004987 requestChildrenUpdate();
4988 }
4989 }
4990
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004991 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek04fb2582015-06-02 19:58:09 +02004992 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004993 int childCount = getChildCount();
4994 for (int i = childCount - 1; i >= 0; i--) {
4995 ExpandableView child = (ExpandableView) getChildAt(i);
4996 if (child.getVisibility() != View.GONE) {
4997 float childTop = child.getY();
4998 if (childTop > touchY) {
4999 // we are above a notification entirely let's abort
5000 return false;
5001 }
Selim Cineka686b2c2016-10-26 13:58:27 -07005002 boolean belowChild = touchY > childTop + child.getActualHeight()
5003 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04005004 if (child == mFooterView) {
Jason Monke59dc402018-08-16 12:05:01 -04005005 if (!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
5006 touchY - childTop)) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01005007 // We clicked on the dismiss button
5008 return false;
5009 }
5010 } else if (child == mEmptyShadeView) {
5011 // We arrived at the empty shade view, for which we accept all clicks
5012 return true;
Jason Monke59dc402018-08-16 12:05:01 -04005013 } else if (!belowChild) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01005014 // We are on a child
5015 return false;
5016 }
5017 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005018 }
Selim Cinek04fb2582015-06-02 19:58:09 +02005019 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005020 }
5021
Selim Cinekc22fff62016-05-20 12:44:30 -07005022 /** @hide */
5023 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005024 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07005025 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
5026 super.onInitializeAccessibilityEventInternal(event);
5027 event.setScrollable(mScrollable);
5028 event.setScrollX(mScrollX);
Selim Cinekc22fff62016-05-20 12:44:30 -07005029 event.setMaxScrollX(mScrollX);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005030 if (ANCHOR_SCROLLING) {
5031 // TODO
5032 } else {
5033 event.setScrollY(mOwnScrollY);
5034 event.setMaxScrollY(getScrollRange());
5035 }
Selim Cinekc22fff62016-05-20 12:44:30 -07005036 }
5037
5038 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005039 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07005040 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
5041 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07005042 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005043 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07005044 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005045 info.addAction(
5046 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
5047 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
5048 }
Selim Cinekef406062016-09-29 17:33:13 -07005049 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005050 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
5051 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
5052 }
5053 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07005054 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
5055 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07005056 }
5057
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005058 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb5605e52015-02-20 18:21:41 +01005059 public void generateChildOrderChangedEvent() {
5060 if (mIsExpanded && mAnimationsEnabled) {
5061 mGenerateChildOrderChangedEvent = true;
5062 mNeedsAnimation = true;
5063 requestChildrenUpdate();
5064 }
5065 }
5066
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005067 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005068 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005069 public int getContainerChildCount() {
5070 return getChildCount();
5071 }
5072
5073 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005074 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005075 public View getContainerChildAt(int i) {
5076 return getChildAt(i);
5077 }
5078
5079 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005080 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005081 public void removeContainerView(View v) {
5082 removeView(v);
5083 }
5084
5085 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005086 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005087 public void addContainerView(View v) {
5088 addView(v);
5089 }
5090
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005091 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek684a4422015-04-15 16:18:39 -07005092 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005093 mAnimationFinishedRunnables.add(runnable);
5094 }
5095
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005096 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
yoshiki iguchi4e30e762018-02-06 12:09:23 +09005097 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005098 mHeadsUpManager = headsUpManager;
Selim Cinek29aab962018-02-27 17:05:45 -08005099 mHeadsUpManager.addListener(mRoundnessManager);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04005100 mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005101 }
5102
Ned Burnsf81c4c42019-01-07 14:10:43 -05005103 public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
Evan Laird94492852018-10-25 13:43:01 -04005104 ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
5105 generateHeadsUpAnimation(row, isHeadsUp);
5106 }
5107
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005108 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005109 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07005110 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005111 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
5112 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07005113 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07005114 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07005115 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005116 requestChildrenUpdate();
5117 }
5118 }
5119
Selim Cineka59ecc32015-04-07 10:51:49 -07005120 /**
5121 * Set the boundary for the bottom heads up position. The heads up will always be above this
5122 * position.
5123 *
Jason Monke59dc402018-08-16 12:05:01 -04005124 * @param height the height of the screen
Selim Cineka59ecc32015-04-07 10:51:49 -07005125 * @param bottomBarHeight the height of the bar on the bottom
5126 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005127 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka59ecc32015-04-07 10:51:49 -07005128 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
5129 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
5130 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005131 requestChildrenUpdate();
5132 }
5133
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005134 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005135 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
5136 mTrackingHeadsUp = row != null;
5137 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005138 }
5139
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005140 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaac93252015-04-14 20:04:12 -07005141 public void setScrimController(ScrimController scrimController) {
5142 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07005143 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07005144 }
5145
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005146 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005147 public void forceNoOverlappingRendering(boolean force) {
5148 mForceNoOverlappingRendering = force;
5149 }
5150
5151 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005152 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005153 public boolean hasOverlappingRendering() {
5154 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
5155 }
5156
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005157 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek6811d722016-01-19 17:53:12 -08005158 public void setAnimationRunning(boolean animationRunning) {
5159 if (animationRunning != mAnimationRunning) {
5160 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07005161 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005162 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07005163 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005164 }
5165 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08005166 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08005167 }
5168 }
5169
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005170 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3776fe02016-02-04 13:32:43 -08005171 public boolean isExpanded() {
5172 return mIsExpanded;
5173 }
5174
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005175 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin4e023812018-04-02 21:19:23 -07005176 public void setPulsing(boolean pulsing, boolean animated) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09005177 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01005178 return;
5179 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005180 mPulsing = pulsing;
Selim Cinekebf42342017-07-13 15:46:10 +02005181 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005182 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08005183 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08005184 updateContentHeight();
Adrian Roosd83e9992017-03-16 15:17:57 -07005185 requestChildrenUpdate();
Lucas Dupin4e023812018-04-02 21:19:23 -07005186 notifyHeightChangeListener(null, animated);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005187 }
5188
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005189 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07005190 public void setQsExpanded(boolean qsExpanded) {
5191 mQsExpanded = qsExpanded;
5192 updateAlgorithmLayoutMinHeight();
Riddle Hsu065c01c2018-05-10 23:14:19 +08005193 updateScrollability();
Selim Cinekbc243a92016-09-27 16:35:13 -07005194 }
5195
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005196 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005197 public void setQsExpansionFraction(float qsExpansionFraction) {
5198 mQsExpansionFraction = qsExpansionFraction;
5199 }
5200
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005201 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Gus Prevas0fa58d62019-01-11 13:58:40 -05005202 private void setOwnScrollY(int ownScrollY) {
5203 assert !ANCHOR_SCROLLING;
Selim Cinekef406062016-09-29 17:33:13 -07005204 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08005205 // We still want to call the normal scrolled changed for accessibility reasons
5206 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07005207 mOwnScrollY = ownScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -05005208 updateOnScrollChange();
5209 }
5210 }
5211
5212 private void updateOnScrollChange() {
5213 updateForwardAndBackwardScrollability();
5214 requestChildrenUpdate();
5215 }
5216
5217 private void updateScrollAnchor() {
5218 int anchorIndex = indexOfChild(mScrollAnchorView);
5219 // If the anchor view has been scrolled off the top, move to the next view.
5220 while (mScrollAnchorViewY < 0) {
5221 View nextAnchor = null;
5222 for (int i = anchorIndex + 1; i < getChildCount(); i++) {
5223 View child = getChildAt(i);
5224 if (child.getVisibility() != View.GONE
5225 && child instanceof ExpandableNotificationRow) {
5226 anchorIndex = i;
5227 nextAnchor = child;
5228 break;
5229 }
5230 }
5231 if (nextAnchor == null) {
5232 break;
5233 }
5234 mScrollAnchorViewY +=
5235 (int) (nextAnchor.getTranslationY() - mScrollAnchorView.getTranslationY());
5236 mScrollAnchorView = nextAnchor;
5237 }
5238 // If the view above the anchor view is fully visible, make it the anchor view.
5239 while (anchorIndex > 0 && mScrollAnchorViewY > 0) {
5240 View prevAnchor = null;
5241 for (int i = anchorIndex - 1; i >= 0; i--) {
5242 View child = getChildAt(i);
5243 if (child.getVisibility() != View.GONE
5244 && child instanceof ExpandableNotificationRow) {
5245 anchorIndex = i;
5246 prevAnchor = child;
5247 break;
5248 }
5249 }
5250 if (prevAnchor == null) {
5251 break;
5252 }
5253 float distanceToPreviousAnchor =
5254 mScrollAnchorView.getTranslationY() - prevAnchor.getTranslationY();
5255 if (distanceToPreviousAnchor < mScrollAnchorViewY) {
5256 mScrollAnchorViewY -= (int) distanceToPreviousAnchor;
5257 mScrollAnchorView = prevAnchor;
5258 }
Selim Cinekef406062016-09-29 17:33:13 -07005259 }
5260 }
5261
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005262 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005263 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07005264 int index = -1;
5265 if (mShelf != null) {
5266 index = indexOfChild(mShelf);
5267 removeView(mShelf);
5268 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08005269 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07005270 addView(mShelf, index);
5271 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07005272 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07005273 shelf.bind(mAmbientState, this);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005274 if (ANCHOR_SCROLLING) {
5275 mScrollAnchorView = mShelf;
5276 }
Selim Cinek281c2022016-10-13 19:14:43 -07005277 }
5278
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005279 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005280 public NotificationShelf getNotificationShelf() {
5281 return mShelf;
5282 }
5283
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005284 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekad7fac02016-10-18 17:09:15 -07005285 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
5286 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
5287 mMaxDisplayedNotifications = maxDisplayedNotifications;
5288 updateContentHeight();
5289 notifyHeightChangeListener(mShelf);
5290 }
5291 }
5292
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005293 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005294 public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
Jason Monke59dc402018-08-16 12:05:01 -04005295 mShouldShowShelfOnly = shouldShowShelfOnly;
shawnlin8e4e92c2018-04-12 18:47:24 +08005296 updateAlgorithmLayoutMinHeight();
5297 }
5298
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005299 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek48ff9b42016-11-09 19:31:51 -08005300 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07005301 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08005302 }
5303
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005304 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005305 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
5306 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
5307 updateClipping();
5308 }
5309
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005310 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005311 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
5312 mHeadsUpAnimatingAway = headsUpAnimatingAway;
5313 updateClipping();
5314 }
5315
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005316 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk297c04e2018-08-23 17:16:59 -04005317 @VisibleForTesting
5318 protected void setStatusBarState(int statusBarState) {
Selim Cinek355652a2016-12-07 13:32:12 -08005319 mStatusBarState = statusBarState;
5320 mAmbientState.setStatusBarState(statusBarState);
Evan Laird91d0f102018-09-18 17:39:55 -04005321 }
5322
5323 private void onStatePostChange() {
Jason Monk1fd3fc32018-08-14 17:20:09 -04005324 boolean onKeyguard = onKeyguard();
5325 boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
Evan Laird91d0f102018-09-18 17:39:55 -04005326
Jason Monk1fd3fc32018-08-14 17:20:09 -04005327 if (mHeadsUpAppearanceController != null) {
5328 mHeadsUpAppearanceController.setPublicMode(publicMode);
5329 }
5330
Beverly8fdb5332019-02-04 14:29:49 -05005331 SysuiStatusBarStateController state = (SysuiStatusBarStateController)
5332 Dependency.get(StatusBarStateController.class);
Jason Monk1fd3fc32018-08-14 17:20:09 -04005333 setHideSensitive(publicMode, state.goingToFullShade() /* animate */);
5334 setDimmed(onKeyguard, state.fromShadeLocked() /* animate */);
5335 setExpandingEnabled(!onKeyguard);
5336 ActivatableNotificationView activatedChild = getActivatedChild();
5337 setActivatedChild(null);
5338 if (activatedChild != null) {
5339 activatedChild.makeInactive(false /* animate */);
5340 }
Jason Monke59dc402018-08-16 12:05:01 -04005341 updateFooter();
Dave Mankoff57445802018-10-10 14:47:34 -04005342 requestChildrenUpdate();
Jason Monke59dc402018-08-16 12:05:01 -04005343 onUpdateRowStates();
Evan Laird91d0f102018-09-18 17:39:55 -04005344
5345 mEntryManager.updateNotifications();
Selim Cinek355652a2016-12-07 13:32:12 -08005346 }
5347
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005348 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd5ab6452016-12-08 16:34:00 -08005349 public void setExpandingVelocity(float expandingVelocity) {
5350 mAmbientState.setExpandingVelocity(expandingVelocity);
5351 }
5352
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005353 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005354 public float getOpeningHeight() {
5355 if (mEmptyShadeView.getVisibility() == GONE) {
5356 return getMinExpansionHeight();
5357 } else {
5358 return getAppearEndPosition();
5359 }
5360 }
5361
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005362 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005363 public void setIsFullWidth(boolean isFullWidth) {
5364 mAmbientState.setPanelFullWidth(isFullWidth);
5365 }
5366
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005367 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekec29d342017-05-05 18:31:49 -07005368 public void setUnlockHintRunning(boolean running) {
5369 mAmbientState.setUnlockHintRunning(running);
5370 }
5371
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005372 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005373 public void setQsCustomizerShowing(boolean isShowing) {
5374 mAmbientState.setQsCustomizerShowing(isShowing);
5375 requestChildrenUpdate();
5376 }
5377
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005378 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005379 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
5380 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
5381 }
5382
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005383 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin0cd882f2018-01-30 12:19:49 -08005384 public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
5385 mAntiBurnInOffsetX = antiBurnInOffsetX;
Bill Line60aa1e2018-06-13 18:07:15 +08005386 updatePanelTranslation();
Adrian Roosdc747bd2017-06-01 16:09:15 -07005387 }
5388
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005389 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek707e2072017-06-30 18:32:40 +02005390 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5391 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
shawnlin8e4e92c2018-04-12 18:47:24 +08005392 + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
5393 + " qsExpandFraction=%f]",
Selim Cinek707e2072017-06-30 18:32:40 +02005394 this.getClass().getSimpleName(),
Jason Monke59dc402018-08-16 12:05:01 -04005395 mPulsing ? "T" : "f",
5396 mAmbientState.isQsCustomizerShowing() ? "T" : "f",
Selim Cinek707e2072017-06-30 18:32:40 +02005397 getVisibility() == View.VISIBLE ? "visible"
5398 : getVisibility() == View.GONE ? "gone"
5399 : "invisible",
5400 getAlpha(),
shawnlin8e4e92c2018-04-12 18:47:24 +08005401 mAmbientState.getScrollY(),
5402 mMaxTopPadding,
Jason Monke59dc402018-08-16 12:05:01 -04005403 mShouldShowShelfOnly ? "T" : "f",
shawnlin8e4e92c2018-04-12 18:47:24 +08005404 mQsExpansionFraction));
Selim Cinek30887662018-10-15 17:37:21 -07005405 int childCount = getChildCount();
5406 pw.println(" Number of children: " + childCount);
5407 pw.println();
5408
5409 for (int i = 0; i < childCount; i++) {
5410 ExpandableView child = (ExpandableView) getChildAt(i);
5411 child.dump(fd, pw, args);
5412 if (!(child instanceof ExpandableNotificationRow)) {
5413 pw.println(" " + child.getClass().getSimpleName());
5414 // Notifications dump it's viewstate as part of their dump to support children
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005415 ExpandableViewState viewState = child.getViewState();
Selim Cinek30887662018-10-15 17:37:21 -07005416 if (viewState == null) {
5417 pw.println(" no viewState!!!");
5418 } else {
5419 pw.print(" ");
5420 viewState.dump(fd, pw, args);
5421 pw.println();
5422 pw.println();
5423 }
5424 }
5425 }
Selim Cinek30887662018-10-15 17:37:21 -07005426 int transientViewCount = getTransientViewCount();
Selim Cinekd4c32302018-11-19 19:43:14 -08005427 pw.println(" Transient Views: " + transientViewCount);
Selim Cinek30887662018-10-15 17:37:21 -07005428 for (int i = 0; i < transientViewCount; i++) {
5429 ExpandableView child = (ExpandableView) getTransientView(i);
5430 child.dump(fd, pw, args);
5431 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005432 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08005433 int draggedCount = draggedViews.size();
5434 pw.println(" Dragged Views: " + draggedCount);
5435 for (int i = 0; i < draggedCount; i++) {
5436 ExpandableView child = (ExpandableView) draggedViews.get(i);
5437 child.dump(fd, pw, args);
5438 }
Selim Cinek707e2072017-06-30 18:32:40 +02005439 }
5440
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005441 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin16cfe452018-02-08 13:14:50 -08005442 public boolean isFullyDark() {
5443 return mAmbientState.isFullyDark();
5444 }
5445
Selim Cinek7103fd42016-05-09 22:22:33 -04005446 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005447 * Add a listener whenever the expanded height changes. The first value passed as an argument
5448 * is the expanded height and the second one is the appearFraction.
5449 *
5450 * @param listener the listener to notify.
5451 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005452 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005453 public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
5454 mExpandedHeightListeners.add(listener);
5455 }
5456
5457 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07005458 * Stop a listener from listening to the expandedHeight.
5459 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005460 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek60ffea62018-03-22 13:16:44 -07005461 public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
5462 mExpandedHeightListeners.remove(listener);
5463 }
5464
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005465 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekf0c79e12018-05-14 17:17:31 -07005466 public void setHeadsUpAppearanceController(
5467 HeadsUpAppearanceController headsUpAppearanceController) {
5468 mHeadsUpAppearanceController = headsUpAppearanceController;
5469 }
5470
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005471 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek9bfc7a52018-06-11 16:09:00 -07005472 public void setIconAreaController(NotificationIconAreaController controller) {
5473 mIconAreaController = controller;
5474 }
5475
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005476 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005477 public void manageNotifications(View v) {
5478 Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
5479 mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
5480 }
5481
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005482 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -04005483 private void clearNotifications(
5484 @SelectedRows int selection,
5485 boolean closeShade) {
Jason Monke59dc402018-08-16 12:05:01 -04005486 // animate-swipe all dismissable notifications, then animate the shade closed
5487 int numChildren = getChildCount();
5488
5489 final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
5490 final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
5491 for (int i = 0; i < numChildren; i++) {
5492 final View child = getChildAt(i);
5493 if (child instanceof ExpandableNotificationRow) {
5494 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
5495 boolean parentVisible = false;
5496 boolean hasClipBounds = child.getClipBounds(mTmpRect);
Ned Burns61269442019-05-02 18:27:23 -04005497 if (includeChildInDismissAll(row, selection)) {
Jason Monke59dc402018-08-16 12:05:01 -04005498 viewsToRemove.add(row);
5499 if (child.getVisibility() == View.VISIBLE
5500 && (!hasClipBounds || mTmpRect.height() > 0)) {
5501 viewsToHide.add(child);
5502 parentVisible = true;
5503 }
5504 } else if (child.getVisibility() == View.VISIBLE
5505 && (!hasClipBounds || mTmpRect.height() > 0)) {
5506 parentVisible = true;
5507 }
5508 List<ExpandableNotificationRow> children = row.getNotificationChildren();
5509 if (children != null) {
5510 for (ExpandableNotificationRow childRow : children) {
Ned Burns61269442019-05-02 18:27:23 -04005511 if (includeChildInDismissAll(row, selection)) {
5512 viewsToRemove.add(childRow);
5513 if (parentVisible && row.areChildrenExpanded()) {
5514 hasClipBounds = childRow.getClipBounds(mTmpRect);
5515 if (childRow.getVisibility() == View.VISIBLE
5516 && (!hasClipBounds || mTmpRect.height() > 0)) {
5517 viewsToHide.add(childRow);
5518 }
Jason Monke59dc402018-08-16 12:05:01 -04005519 }
5520 }
5521 }
5522 }
5523 }
5524 }
Ned Burns61269442019-05-02 18:27:23 -04005525
Jason Monke59dc402018-08-16 12:05:01 -04005526 if (viewsToRemove.isEmpty()) {
Ned Burns61269442019-05-02 18:27:23 -04005527 if (closeShade) {
5528 mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
5529 }
Jason Monke59dc402018-08-16 12:05:01 -04005530 return;
5531 }
5532
Ned Burns61269442019-05-02 18:27:23 -04005533 performDismissAllAnimations(viewsToHide, closeShade, () -> {
Jason Monke59dc402018-08-16 12:05:01 -04005534 for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
Ned Burns61269442019-05-02 18:27:23 -04005535 if (StackScrollAlgorithm.canChildBeDismissed(rowToRemove)) {
5536 if (selection == ROWS_ALL) {
5537 // TODO: This is a listener method; we shouldn't be calling it. Can we just
5538 // call performRemoveNotification as below?
5539 mEntryManager.removeNotification(
5540 rowToRemove.getEntry().key,
5541 null /* ranking */,
5542 NotificationListenerService.REASON_CANCEL_ALL);
5543 } else {
5544 mEntryManager.performRemoveNotification(
5545 rowToRemove.getEntry().notification,
5546 NotificationListenerService.REASON_CANCEL_ALL);
5547 }
Jason Monke59dc402018-08-16 12:05:01 -04005548 } else {
5549 rowToRemove.resetTranslation();
5550 }
5551 }
Ned Burns61269442019-05-02 18:27:23 -04005552 if (selection == ROWS_ALL) {
5553 try {
5554 mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
5555 } catch (Exception ex) {
5556 }
Jason Monke59dc402018-08-16 12:05:01 -04005557 }
5558 });
Jason Monke59dc402018-08-16 12:05:01 -04005559 }
5560
Ned Burns61269442019-05-02 18:27:23 -04005561 private boolean includeChildInDismissAll(
5562 ExpandableNotificationRow row,
5563 @SelectedRows int selection) {
5564 return StackScrollAlgorithm.canChildBeDismissed(row) && matchesSelection(row, selection);
5565 }
5566
5567 /**
5568 * Given a list of rows, animates them away in a staggered fashion as if they were dismissed.
5569 * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete
5570 * handler.
5571 *
5572 * @param hideAnimatedList List of rows to animated away. Should only be views that are
5573 * currently visible, or else the stagger will look funky.
5574 * @param closeShade Whether to close the shade after the stagger animation completes.
5575 * @param onAnimationComplete Called after the entire animation completes (including the shade
5576 * closing if appropriate). The rows must be dismissed for real here.
5577 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005578 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -04005579 private void performDismissAllAnimations(
5580 final ArrayList<View> hideAnimatedList,
5581 final boolean closeShade,
5582 final Runnable onAnimationComplete) {
5583
5584 final Runnable onSlideAwayAnimationComplete = () -> {
5585 if (closeShade) {
5586 mShadeController.addPostCollapseAction(() -> {
5587 setDismissAllInProgress(false);
5588 onAnimationComplete.run();
5589 });
5590 mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
5591 } else {
5592 setDismissAllInProgress(false);
5593 onAnimationComplete.run();
5594 }
Jason Monke59dc402018-08-16 12:05:01 -04005595 };
5596
5597 if (hideAnimatedList.isEmpty()) {
Ned Burns61269442019-05-02 18:27:23 -04005598 onSlideAwayAnimationComplete.run();
Jason Monke59dc402018-08-16 12:05:01 -04005599 return;
5600 }
5601
5602 // let's disable our normal animations
5603 setDismissAllInProgress(true);
5604
5605 // Decrease the delay for every row we animate to give the sense of
5606 // accelerating the swipes
5607 int rowDelayDecrement = 10;
5608 int currentDelay = 140;
5609 int totalDelay = 180;
5610 int numItems = hideAnimatedList.size();
5611 for (int i = numItems - 1; i >= 0; i--) {
5612 View view = hideAnimatedList.get(i);
5613 Runnable endRunnable = null;
5614 if (i == 0) {
Ned Burns61269442019-05-02 18:27:23 -04005615 endRunnable = onSlideAwayAnimationComplete;
Jason Monke59dc402018-08-16 12:05:01 -04005616 }
Lucas Dupinfb8bdbb2018-12-02 15:09:37 -08005617 dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
Jason Monke59dc402018-08-16 12:05:01 -04005618 currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
5619 totalDelay += currentDelay;
5620 }
5621 }
5622
5623 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005624 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005625 protected void inflateFooterView() {
5626 FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
5627 R.layout.status_bar_notification_footer, this, false);
5628 footerView.setDismissButtonClickListener(v -> {
5629 mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
Ned Burns61269442019-05-02 18:27:23 -04005630 clearNotifications(ROWS_ALL, true /* closeShade */);
Jason Monke59dc402018-08-16 12:05:01 -04005631 });
5632 footerView.setManageButtonClickListener(this::manageNotifications);
5633 setFooterView(footerView);
5634 }
5635
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005636 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5637 private void inflateEmptyShadeView() {
Jason Monke59dc402018-08-16 12:05:01 -04005638 EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
5639 R.layout.status_bar_no_notifications, this, false);
5640 view.setText(R.string.empty_shade_text);
5641 setEmptyShadeView(view);
5642 }
5643
5644 /**
5645 * Updates expanded, dimmed and locked states of notification rows.
5646 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005647 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jason Monke59dc402018-08-16 12:05:01 -04005648 public void onUpdateRowStates() {
5649 changeViewPosition(mFooterView, -1);
5650
5651 // The following views will be moved to the end of mStackScroller. This counter represents
5652 // the offset from the last child. Initialized to 1 for the very last position. It is post-
5653 // incremented in the following "changeViewPosition" calls so that its value is correct for
5654 // subsequent calls.
5655 int offsetFromEnd = 1;
5656 changeViewPosition(mEmptyShadeView,
5657 getChildCount() - offsetFromEnd++);
5658
5659 // No post-increment for this call because it is the last one. Make sure to add one if
5660 // another "changeViewPosition" call is ever added.
5661 changeViewPosition(mShelf,
5662 getChildCount() - offsetFromEnd);
Jason Monke59dc402018-08-16 12:05:01 -04005663 }
5664
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005665 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5666 public void setNotificationPanel(NotificationPanelView notificationPanelView) {
Jason Monke59dc402018-08-16 12:05:01 -04005667 mNotificationPanel = notificationPanelView;
5668 }
5669
Jason Monk297c04e2018-08-23 17:16:59 -04005670 public void updateIconAreaViews() {
5671 mIconAreaController.updateNotificationIcons();
5672 }
5673
Selim Cinek60ffea62018-03-22 13:16:44 -07005674 /**
Selim Cinek3d6ae232019-01-04 14:14:33 -08005675 * Set how far the wake up is when waking up from pulsing. This is a height and will adjust the
5676 * notification positions accordingly.
5677 * @param height the new wake up height
5678 * @return the overflow how much the height is further than he lowest notification
5679 */
Selim Cinek5040f2e2019-02-14 18:22:42 -08005680 public float setPulseHeight(float height) {
5681 mAmbientState.setPulseHeight(height);
Selim Cinek3d6ae232019-01-04 14:14:33 -08005682 requestChildrenUpdate();
5683 return Math.max(0, height - mAmbientState.getInnerHeight(true /* ignorePulseHeight */));
5684 }
5685
5686 /**
5687 * Set the amount how much we're dozing. This is different from how dark the shade is, when
5688 * the notification is pulsing.
5689 */
5690 public void setDozeAmount(float dozeAmount) {
5691 mAmbientState.setDozeAmount(dozeAmount);
Selim Cinekae55d832019-02-22 17:43:43 -08005692 updateContinuousBackgroundDrawing();
Selim Cinek3d6ae232019-01-04 14:14:33 -08005693 requestChildrenUpdate();
5694 }
5695
Selim Cinek459aee32019-02-20 11:18:56 -08005696 public void wakeUpFromPulse() {
Selim Cinek34518f62019-02-28 19:41:18 -08005697 setPulseHeight(getPulseHeight());
Selim Cinek459aee32019-02-20 11:18:56 -08005698 // Let's place the hidden views at the end of the pulsing notification to make sure we have
5699 // a smooth animation
5700 boolean firstVisibleView = true;
5701 float wakeUplocation = -1f;
5702 int childCount = getChildCount();
5703 for (int i = 0; i < childCount; i++) {
5704 ExpandableView view = (ExpandableView) getChildAt(i);
5705 if (view.getVisibility() == View.GONE) {
5706 continue;
5707 }
5708 boolean isShelf = view == mShelf;
5709 if (!(view instanceof ExpandableNotificationRow) && !isShelf) {
5710 continue;
5711 }
5712 if (view.getVisibility() == View.VISIBLE && !isShelf) {
5713 if (firstVisibleView) {
5714 firstVisibleView = false;
5715 wakeUplocation = view.getTranslationY()
5716 + view.getActualHeight() - mShelf.getIntrinsicHeight();
5717 }
5718 } else if (!firstVisibleView) {
5719 view.setTranslationY(wakeUplocation);
5720 }
5721 }
5722 }
5723
Selim Cinek6f0a62a2019-04-09 18:40:12 -07005724 @Override
5725 public void onDynamicPrivacyChanged() {
5726 if (mIsExpanded) {
5727 // The bottom might change because we're using the final actual height of the view
5728 mAnimateBottomOnLayout = true;
5729 }
5730 }
5731
Selim Cinek3d6ae232019-01-04 14:14:33 -08005732 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005733 * A listener that is notified when the empty space below the notifications is clicked on
5734 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005735 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005736 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08005737 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005738 }
5739
5740 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02005741 * A listener that gets notified when the overscroll at the top has changed.
5742 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005743 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi290600a2014-05-30 17:02:20 +02005744 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02005745
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005746 /**
5747 * Notifies a listener that the overscroll has changed.
5748 *
5749 * @param amount the amount of overscroll, in pixels
5750 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
5751 * unrubberbanded motion to directly expand overscroll view (e.g
5752 * expand
5753 * QS)
5754 */
5755 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02005756
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005757 /**
5758 * Notify a listener that the scroller wants to escape from the scrolling motion and
5759 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
5760 *
5761 * @param velocity The velocity that the Scroller had when over flinging
5762 * @param open Should the fling open or close the overscroll view.
5763 */
5764 void flingTopOverscroll(float velocity, boolean open);
5765 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02005766
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005767 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5768 public boolean hasActiveNotifications() {
Jason Monke59dc402018-08-16 12:05:01 -04005769 return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
5770 }
5771
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04005772 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005773 public void updateSpeedBumpIndex() {
Jason Monke59dc402018-08-16 12:05:01 -04005774 int speedBumpIndex = 0;
5775 int currentIndex = 0;
5776 final int N = getChildCount();
5777 for (int i = 0; i < N; i++) {
5778 View view = getChildAt(i);
5779 if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
5780 continue;
5781 }
5782 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
5783 currentIndex++;
Gus Prevas33619af2018-10-26 15:40:27 -04005784 boolean beforeSpeedBump;
5785 if (mLowPriorityBeforeSpeedBump) {
Gus Prevascaed15c2019-01-18 14:19:51 -05005786 beforeSpeedBump = !row.getEntry().ambient;
Gus Prevas33619af2018-10-26 15:40:27 -04005787 } else {
Gus Prevascaed15c2019-01-18 14:19:51 -05005788 beforeSpeedBump = row.getEntry().isHighPriority();
Gus Prevas33619af2018-10-26 15:40:27 -04005789 }
5790 if (beforeSpeedBump) {
Jason Monke59dc402018-08-16 12:05:01 -04005791 speedBumpIndex = currentIndex;
5792 }
5793 }
5794 boolean noAmbient = speedBumpIndex == N;
5795 updateSpeedBumpIndex(speedBumpIndex, noAmbient);
5796 }
5797
Gus Prevase2d6f042018-10-17 15:25:30 -04005798 /** Updates the indices of the boundaries between sections. */
5799 @ShadeViewRefactor(RefactorComponent.INPUT)
5800 public void updateSectionBoundaries() {
Ned Burns9eb06332019-04-23 16:02:12 -04005801 mSectionsManager.updateSectionBoundaries();
Gus Prevase2d6f042018-10-17 15:25:30 -04005802 }
5803
Selim Cinekae55d832019-02-22 17:43:43 -08005804 private void updateContinuousBackgroundDrawing() {
5805 boolean continuousBackground = !mAmbientState.isFullyAwake()
5806 && !mAmbientState.getDraggedViews().isEmpty();
5807 if (continuousBackground != mContinuousBackgroundUpdate) {
5808 mContinuousBackgroundUpdate = continuousBackground;
5809 if (continuousBackground) {
5810 getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater);
5811 } else {
5812 getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater);
5813 }
5814 }
5815 }
5816
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005817 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08005818 private void updateContinuousShadowDrawing() {
5819 boolean continuousShadowUpdate = mAnimationRunning
5820 || !mAmbientState.getDraggedViews().isEmpty();
5821 if (continuousShadowUpdate != mContinuousShadowUpdate) {
5822 if (continuousShadowUpdate) {
5823 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
5824 } else {
5825 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
5826 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07005827 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08005828 }
5829 }
5830
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005831 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005832 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -08005833 public void resetExposedMenuView(boolean animate, boolean force) {
5834 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07005835 }
5836
Ned Burns61269442019-05-02 18:27:23 -04005837 private static boolean matchesSelection(
5838 ExpandableNotificationRow row,
5839 @SelectedRows int selection) {
5840 switch (selection) {
5841 case ROWS_ALL:
5842 return true;
5843 case ROWS_HIGH_PRIORITY:
5844 return row.getEntry().isHighPriority();
5845 case ROWS_GENTLE:
5846 return !row.getEntry().isHighPriority();
5847 default:
5848 throw new IllegalArgumentException("Unknown selection: " + selection);
5849 }
5850 }
5851
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005852 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
5853 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02005854
Jason Monke59dc402018-08-16 12:05:01 -04005855 static AnimationFilter[] FILTERS = new AnimationFilter[]{
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005856
5857 // ANIMATION_TYPE_ADD
5858 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005859 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005860 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005861 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005862 .animateZ()
5863 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005864
5865 // ANIMATION_TYPE_REMOVE
5866 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005867 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005868 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005869 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005870 .animateZ()
5871 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005872
5873 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5874 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005875 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005876 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005877 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005878 .animateZ()
5879 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005880
5881 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5882 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005883 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005884 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005885 .animateY()
5886 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005887 .animateZ(),
5888
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005889 // ANIMATION_TYPE_ACTIVATED_CHILD
5890 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005891 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005892
5893 // ANIMATION_TYPE_DIMMED
5894 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005895 .animateDimmed(),
5896
5897 // ANIMATION_TYPE_CHANGE_POSITION
5898 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005899 .animateAlpha() // maybe the children change positions
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005900 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005901 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005902 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04005903 .animateZ(),
5904
5905 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07005906 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005907
5908 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5909 new AnimationFilter()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005910 .animateHeight()
5911 .animateTopInset()
5912 .animateY()
5913 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02005914 .animateZ()
5915 .hasDelays(),
5916
5917 // ANIMATION_TYPE_HIDE_SENSITIVE
5918 new AnimationFilter()
5919 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02005920
5921 // ANIMATION_TYPE_VIEW_RESIZE
5922 new AnimationFilter()
Selim Cineka5e211b2014-08-11 17:35:48 +02005923 .animateHeight()
5924 .animateTopInset()
5925 .animateY()
5926 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02005927
Selim Cinekb5605e52015-02-20 18:21:41 +01005928 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
5929 new AnimationFilter()
5930 .animateAlpha()
5931 .animateHeight()
5932 .animateTopInset()
5933 .animateY()
5934 .animateZ(),
5935
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005936 // ANIMATION_TYPE_HEADS_UP_APPEAR
5937 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005938 .animateHeight()
5939 .animateTopInset()
5940 .animateY()
5941 .animateZ(),
5942
5943 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5944 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005945 .animateHeight()
5946 .animateTopInset()
5947 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07005948 .animateZ()
5949 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005950
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005951 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5952 new AnimationFilter()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005953 .animateHeight()
5954 .animateTopInset()
5955 .animateY()
5956 .animateZ()
5957 .hasDelays(),
5958
Selim Cineka59ecc32015-04-07 10:51:49 -07005959 // ANIMATION_TYPE_HEADS_UP_OTHER
5960 new AnimationFilter()
Selim Cineka59ecc32015-04-07 10:51:49 -07005961 .animateHeight()
5962 .animateTopInset()
5963 .animateY()
5964 .animateZ(),
5965
Selim Cinekd9acca52014-09-01 22:33:25 +02005966 // ANIMATION_TYPE_EVERYTHING
5967 new AnimationFilter()
5968 .animateAlpha()
5969 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02005970 .animateDimmed()
5971 .animateHideSensitive()
5972 .animateHeight()
5973 .animateTopInset()
5974 .animateY()
5975 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005976 };
5977
Jason Monke59dc402018-08-16 12:05:01 -04005978 static int[] LENGTHS = new int[]{
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005979
5980 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005981 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005982
5983 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005984 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005985
5986 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5987 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5988
5989 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5990 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5991
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005992 // ANIMATION_TYPE_ACTIVATED_CHILD
5993 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
5994
5995 // ANIMATION_TYPE_DIMMED
5996 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005997
5998 // ANIMATION_TYPE_CHANGE_POSITION
5999 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04006000
6001 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07006002 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006003
6004 // ANIMATION_TYPE_GO_TO_FULL_SHADE
6005 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02006006
6007 // ANIMATION_TYPE_HIDE_SENSITIVE
6008 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02006009
6010 // ANIMATION_TYPE_VIEW_RESIZE
6011 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02006012
Selim Cinekb5605e52015-02-20 18:21:41 +01006013 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08006014 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01006015
Selim Cinekb8f09cf2015-03-16 17:09:28 -07006016 // ANIMATION_TYPE_HEADS_UP_APPEAR
6017 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
6018
6019 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
6020 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
6021
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07006022 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
6023 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
6024
Selim Cineka59ecc32015-04-07 10:51:49 -07006025 // ANIMATION_TYPE_HEADS_UP_OTHER
6026 StackStateAnimator.ANIMATION_DURATION_STANDARD,
6027
Selim Cinekd9acca52014-09-01 22:33:25 +02006028 // ANIMATION_TYPE_EVERYTHING
6029 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006030 };
6031
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006032 static final int ANIMATION_TYPE_ADD = 0;
6033 static final int ANIMATION_TYPE_REMOVE = 1;
6034 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
6035 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
Selim Cinekff2ffec2018-11-19 18:52:01 -08006036 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 4;
6037 static final int ANIMATION_TYPE_DIMMED = 5;
6038 static final int ANIMATION_TYPE_CHANGE_POSITION = 6;
6039 static final int ANIMATION_TYPE_DARK = 7;
6040 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 8;
6041 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 9;
6042 static final int ANIMATION_TYPE_VIEW_RESIZE = 10;
6043 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 11;
6044 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 12;
6045 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 13;
6046 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 14;
6047 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 15;
6048 static final int ANIMATION_TYPE_EVERYTHING = 16;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02006049
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01006050 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
6051 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
6052
Selim Cinek572bbd42014-04-25 16:43:27 +02006053 final long eventStartTime;
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006054 final ExpandableView mChangingView;
Selim Cinek572bbd42014-04-25 16:43:27 +02006055 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02006056 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006057 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006058 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01006059 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07006060 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02006061
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006062 AnimationEvent(ExpandableView view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006063 this(view, type, LENGTHS[type]);
6064 }
6065
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006066 AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006067 this(view, type, LENGTHS[type], filter);
6068 }
6069
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006070 AnimationEvent(ExpandableView view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006071 this(view, type, length, FILTERS[type]);
6072 }
6073
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006074 AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02006075 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006076 mChangingView = view;
Selim Cinek572bbd42014-04-25 16:43:27 +02006077 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006078 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07006079 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006080 }
6081
6082 /**
6083 * Combines the length of several animation events into a single value.
6084 *
6085 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006086 * @return The combined length. Depending on the event types, this might be the maximum of
Jason Monke59dc402018-08-16 12:05:01 -04006087 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006088 */
6089 static long combineLength(ArrayList<AnimationEvent> events) {
6090 long length = 0;
6091 int size = events.size();
6092 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006093 AnimationEvent event = events.get(i);
6094 length = Math.max(length, event.length);
6095 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
6096 return event.length;
6097 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006098 }
6099 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02006100 }
6101 }
Jason Monke59dc402018-08-16 12:05:01 -04006102
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006103 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
6104 private final StateListener mStateListener = new StateListener() {
Jason Monke59dc402018-08-16 12:05:01 -04006105 @Override
6106 public void onStatePreChange(int oldState, int newState) {
6107 if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
6108 requestAnimateEverything();
6109 }
6110 }
6111
6112 @Override
6113 public void onStateChanged(int newState) {
6114 setStatusBarState(newState);
6115 }
Evan Laird91d0f102018-09-18 17:39:55 -04006116
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006117 @Override
6118 public void onStatePostChange() {
Evan Laird91d0f102018-09-18 17:39:55 -04006119 NotificationStackScrollLayout.this.onStatePostChange();
6120 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006121 };
6122
Will Brockmane718d582019-01-17 16:38:38 -05006123 @VisibleForTesting
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006124 @ShadeViewRefactor(RefactorComponent.INPUT)
Will Brockmane718d582019-01-17 16:38:38 -05006125 protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006126 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006127 public void onMenuClicked(View view, int x, int y, MenuItem item) {
6128 if (mLongPressListener == null) {
6129 return;
6130 }
6131 if (view instanceof ExpandableNotificationRow) {
6132 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Will Brockmane718d582019-01-17 16:38:38 -05006133 mMetricsLogger.write(row.getStatusBarNotification().getLogMaker()
6134 .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
6135 .setType(MetricsEvent.TYPE_ACTION)
6136 );
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006137 }
6138 mLongPressListener.onLongPress(view, x, y, item);
6139 }
6140
6141 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006142 public void onMenuReset(View row) {
6143 View translatingParentView = mSwipeHelper.getTranslatingParentView();
6144 if (translatingParentView != null && row == translatingParentView) {
6145 mSwipeHelper.clearExposedMenuView();
6146 mSwipeHelper.clearTranslatingParentView();
Gus Prevas211181532018-12-13 14:49:33 -05006147 if (row instanceof ExpandableNotificationRow) {
6148 mHeadsUpManager.setMenuShown(
6149 ((ExpandableNotificationRow) row).getEntry(), false);
6150
6151 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006152 }
6153 }
6154
6155 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006156 public void onMenuShown(View row) {
6157 if (row instanceof ExpandableNotificationRow) {
Gus Prevas211181532018-12-13 14:49:33 -05006158 ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
Will Brockmane718d582019-01-17 16:38:38 -05006159 mMetricsLogger.write(notificationRow.getStatusBarNotification().getLogMaker()
6160 .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
6161 .setType(MetricsEvent.TYPE_ACTION));
Gus Prevas211181532018-12-13 14:49:33 -05006162 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
Evan Lairde55c6012019-03-13 12:54:37 -04006163 mSwipeHelper.onMenuShown(row);
6164
6165 // Check to see if we want to go directly to the notfication guts
6166 NotificationMenuRowPlugin provider = notificationRow.getProvider();
6167 if (provider.shouldShowGutsOnSnapOpen()) {
6168 MenuItem item = provider.menuItemToExposeOnSnap();
6169 if (item != null) {
6170 Point origin = provider.getRevealAnimationOrigin();
6171 mGutsManager.openGuts(row, origin.x, origin.y, item);
6172 } else {
6173 Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
6174 + "menu item in menuItemtoExposeOnSnap. Skipping.");
6175 }
6176
6177 // Close the menu row since we went directly to the guts
6178 resetExposedMenuView(false, true);
6179 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006180 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006181 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006182 };
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006183
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006184 @ShadeViewRefactor(RefactorComponent.INPUT)
6185 private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
6186 new NotificationSwipeHelper.NotificationCallback() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006187 @Override
6188 public void onDismiss() {
6189 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
6190 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
6191 false /* resetMenu */);
6192 }
6193
6194 @Override
6195 public void onSnooze(StatusBarNotification sbn,
6196 NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
6197 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
6198 }
6199
6200 @Override
Selim Cinekae55d832019-02-22 17:43:43 -08006201 public boolean shouldDismissQuickly() {
6202 return NotificationStackScrollLayout.this.isExpanded() && mAmbientState.isFullyAwake();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006203 }
6204
6205 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006206 public void onDragCancelled(View v) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006207 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006208 mFalsingManager.onNotificatonStopDismissing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006209 }
6210
6211 /**
6212 * Handles cleanup after the given {@code view} has been fully swiped out (including
6213 * re-invoking dismiss logic in case the notification has not made its way out yet).
6214 */
6215 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006216 public void onChildDismissed(View view) {
6217 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6218 if (!row.isDismissed()) {
6219 handleChildViewDismissed(view);
6220 }
6221 ViewGroup transientContainer = row.getTransientContainer();
6222 if (transientContainer != null) {
6223 transientContainer.removeTransientView(view);
6224 }
6225 }
6226
6227 /**
6228 * Starts up notification dismiss and tells the notification, if any, to remove itself from
6229 * layout.
6230 *
6231 * @param view view (e.g. notification) to dismiss from the layout
6232 */
6233
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006234 public void handleChildViewDismissed(View view) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006235 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006236 if (mDismissAllInProgress) {
6237 return;
6238 }
6239
6240 boolean isBlockingHelperShown = false;
6241
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006242 mAmbientState.onDragFinished(view);
6243 updateContinuousShadowDrawing();
6244
6245 if (view instanceof ExpandableNotificationRow) {
6246 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6247 if (row.isHeadsUp()) {
6248 mHeadsUpManager.addSwipedOutNotification(
6249 row.getStatusBarNotification().getKey());
6250 }
6251 isBlockingHelperShown =
6252 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
6253 }
6254
6255 if (!isBlockingHelperShown) {
6256 mSwipedOutViews.add(view);
6257 }
6258 mFalsingManager.onNotificationDismissed();
6259 if (mFalsingManager.shouldEnforceBouncer()) {
6260 mStatusBar.executeRunnableDismissingKeyguard(
6261 null,
6262 null /* cancelAction */,
6263 false /* dismissShade */,
6264 true /* afterKeyguardGone */,
6265 false /* deferred */);
6266 }
6267 }
6268
6269 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006270 public boolean isAntiFalsingNeeded() {
6271 return onKeyguard();
6272 }
6273
6274 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006275 public View getChildAtPosition(MotionEvent ev) {
6276 View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
6277 ev.getY());
6278 if (child instanceof ExpandableNotificationRow) {
6279 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
6280 ExpandableNotificationRow parent = row.getNotificationParent();
6281 if (parent != null && parent.areChildrenExpanded()
6282 && (parent.areGutsExposed()
6283 || mSwipeHelper.getExposedMenuView() == parent
6284 || (parent.getNotificationChildren().size() == 1
Evan Laird94492852018-10-25 13:43:01 -04006285 && parent.getEntry().isClearable()))) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006286 // In this case the group is expanded and showing the menu for the
6287 // group, further interaction should apply to the group, not any
6288 // child notifications so we use the parent of the child. We also do the same
6289 // if we only have a single child.
6290 child = parent;
6291 }
6292 }
6293 return child;
6294 }
6295
6296 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006297 public void onBeginDrag(View v) {
6298 mFalsingManager.onNotificatonStartDismissing();
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006299 setSwipingInProgress(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006300 mAmbientState.onBeginDrag((ExpandableView) v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006301 updateContinuousShadowDrawing();
Selim Cinekae55d832019-02-22 17:43:43 -08006302 updateContinuousBackgroundDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006303 requestChildrenUpdate();
6304 }
6305
6306 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006307 public void onChildSnappedBack(View animView, float targetLeft) {
6308 mAmbientState.onDragFinished(animView);
6309 updateContinuousShadowDrawing();
Selim Cinekae55d832019-02-22 17:43:43 -08006310 updateContinuousBackgroundDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006311 }
6312
6313 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006314 public boolean updateSwipeProgress(View animView, boolean dismissable,
6315 float swipeProgress) {
6316 // Returning true prevents alpha fading.
6317 return !mFadeNotificationsOnDismiss;
6318 }
6319
6320 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006321 public float getFalsingThresholdFactor() {
6322 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
6323 }
6324
6325 @Override
Gus Prevasc4e68d42019-01-17 15:45:21 -05006326 public int getConstrainSwipeStartPosition() {
6327 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
6328 if (menuRow != null) {
6329 return Math.abs(menuRow.getMenuSnapTarget());
6330 }
6331 return 0;
6332 }
6333
6334 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006335 public boolean canChildBeDismissed(View v) {
Ned Burns61269442019-05-02 18:27:23 -04006336 return StackScrollAlgorithm.canChildBeDismissed(v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006337 }
Gus Prevas37d67e22018-11-02 14:48:55 -04006338
6339 @Override
6340 public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
Evan Laird30b9b162019-04-24 15:22:24 -04006341 //TODO: b/131242807 for why this doesn't do anything with direction
6342 return canChildBeDismissed(v);
Gus Prevas37d67e22018-11-02 14:48:55 -04006343 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006344 };
6345
6346 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
6347
6348 @ShadeViewRefactor(RefactorComponent.INPUT)
6349 private final DragDownCallback mDragDownCallback = new DragDownCallback() {
6350
6351 /* Only ever called as a consequence of a lockscreen expansion gesture. */
6352 @Override
6353 public boolean onDraggedDown(View startingChild, int dragLengthY) {
6354 if (mStatusBarState == StatusBarState.KEYGUARD
Lucas Dupin55c6e802018-09-27 18:07:36 -07006355 && hasActiveNotifications()) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006356 mLockscreenGestureLogger.write(
6357 MetricsEvent.ACTION_LS_SHADE,
6358 (int) (dragLengthY / mDisplayMetrics.density),
6359 0 /* velocityDp - N/A */);
6360
Lucas Dupinc7804042018-12-21 12:26:33 -08006361 if (!mAmbientState.isDark() || startingChild != null) {
Lucas Dupin55c6e802018-09-27 18:07:36 -07006362 // We have notifications, go to locked shade.
Jason Monk297c04e2018-08-23 17:16:59 -04006363 mShadeController.goToLockedShade(startingChild);
Lucas Dupin55c6e802018-09-27 18:07:36 -07006364 if (startingChild instanceof ExpandableNotificationRow) {
6365 ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
6366 row.onExpandedByGesture(true /* drag down is always an open */);
6367 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006368 }
Lucas Dupin55c6e802018-09-27 18:07:36 -07006369
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006370 return true;
6371 } else {
6372 // abort gesture.
6373 return false;
6374 }
6375 }
6376
6377 @Override
6378 public void onDragDownReset() {
6379 setDimmed(true /* dimmed */, true /* animated */);
6380 resetScrollPosition();
6381 resetCheckSnoozeLeavebehind();
6382 }
6383
6384 @Override
6385 public void onCrossedThreshold(boolean above) {
6386 setDimmed(!above /* dimmed */, true /* animate */);
6387 }
6388
6389 @Override
6390 public void onTouchSlopExceeded() {
6391 cancelLongPress();
6392 checkSnoozeLeavebehind();
6393 }
6394
6395 @Override
6396 public void setEmptyDragAmount(float amount) {
6397 mNotificationPanel.setEmptyDragAmount(amount);
6398 }
6399
6400 @Override
6401 public boolean isFalsingCheckNeeded() {
6402 return mStatusBarState == StatusBarState.KEYGUARD;
6403 }
6404 };
6405
6406 public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
6407
6408 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6409 private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
6410 @Override
6411 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6412 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6413 }
6414
6415 @Override
6416 public boolean isExpanded() {
6417 return mIsExpanded;
6418 }
6419
6420 @Override
6421 public Context getContext() {
6422 return mContext;
6423 }
6424 };
6425
6426 public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
6427
6428
6429 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6430 private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
6431 @Override
6432 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
6433 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
6434 && (mIsExpanded || changedRow.isPinned());
6435 if (animated) {
6436 mExpandedGroupView = changedRow;
6437 mNeedsAnimation = true;
6438 }
6439 changedRow.setChildrenExpanded(expanded, animated);
6440 if (!mGroupExpandedForMeasure) {
6441 onHeightChanged(changedRow, false /* needsAnimation */);
6442 }
6443 runAfterAnimationFinished(new Runnable() {
6444 @Override
6445 public void run() {
6446 changedRow.onFinishedExpansionChange();
6447 }
6448 });
6449 }
6450
6451 @Override
6452 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
6453 mStatusBar.requestNotificationUpdate();
6454 }
6455
6456 @Override
6457 public void onGroupsChanged() {
6458 mStatusBar.requestNotificationUpdate();
6459 }
6460 };
6461
6462 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6463 private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
6464 @Override
6465 public ExpandableView getChildAtPosition(float touchX, float touchY) {
6466 return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY);
6467 }
6468
6469 @Override
6470 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6471 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6472 }
6473
6474 @Override
6475 public boolean canChildBeExpanded(View v) {
6476 return v instanceof ExpandableNotificationRow
6477 && ((ExpandableNotificationRow) v).isExpandable()
6478 && !((ExpandableNotificationRow) v).areGutsExposed()
6479 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
6480 }
6481
6482 /* Only ever called as a consequence of an expansion gesture in the shade. */
6483 @Override
6484 public void setUserExpandedChild(View v, boolean userExpanded) {
6485 if (v instanceof ExpandableNotificationRow) {
6486 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
6487 if (userExpanded && onKeyguard()) {
6488 // Due to a race when locking the screen while touching, a notification may be
6489 // expanded even after we went back to keyguard. An example of this happens if
6490 // you click in the empty space while expanding a group.
6491
6492 // We also need to un-user lock it here, since otherwise the content height
6493 // calculated might be wrong. We also can't invert the two calls since
6494 // un-userlocking it will trigger a layout switch in the content view.
6495 row.setUserLocked(false);
6496 updateContentHeight();
6497 notifyHeightChangeListener(row);
6498 return;
6499 }
6500 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
6501 row.onExpandedByGesture(userExpanded);
6502 }
6503 }
6504
6505 @Override
6506 public void setExpansionCancelled(View v) {
6507 if (v instanceof ExpandableNotificationRow) {
6508 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
6509 }
6510 }
6511
6512 @Override
6513 public void setUserLockedChild(View v, boolean userLocked) {
6514 if (v instanceof ExpandableNotificationRow) {
6515 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
6516 }
6517 cancelLongPress();
6518 requestDisallowInterceptTouchEvent(true);
6519 }
6520
6521 @Override
6522 public void expansionStateChanged(boolean isExpanding) {
6523 mExpandingNotification = isExpanding;
6524 if (!mExpandedInThisMotion) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05006525 if (ANCHOR_SCROLLING) {
6526 // TODO
6527 } else {
6528 mMaxScrollAfterExpand = mOwnScrollY;
6529 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006530 mExpandedInThisMotion = true;
6531 }
6532 }
6533
6534 @Override
6535 public int getMaxExpandHeight(ExpandableView view) {
6536 return view.getMaxContentHeight();
6537 }
6538 };
6539
6540 public ExpandHelper.Callback getExpandHelperCallback() {
6541 return mExpandHelperCallback;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006542 }
Ned Burns61269442019-05-02 18:27:23 -04006543
6544 /** Enum for selecting some or all notification rows (does not included non-notif views). */
6545 @Retention(SOURCE)
6546 @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
6547 public @interface SelectedRows {}
6548 /** All rows representing notifs. */
6549 public static final int ROWS_ALL = 0;
6550 /** Only rows where entry.isHighPriority() is true. */
6551 public static final int ROWS_HIGH_PRIORITY = 1;
6552 /** Only rows where entry.isHighPriority() is false. */
6553 public static final int ROWS_GENTLE = 2;
Selim Cinek67b22602014-03-10 15:40:16 +01006554}