blob: 6e1d512e8eda3a7fa7514c4096d607cfda4c9fb6 [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;
Jason Monke59dc402018-08-16 12:05:01 -040091import com.android.systemui.colorextraction.SysuiColorExtractor;
Ned Burns9eb06332019-04-23 16:02:12 -040092import com.android.systemui.plugins.ActivityStarter;
Dave Mankoff468d4f62019-05-08 14:56:29 -040093import com.android.systemui.plugins.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080094import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
95import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -040096import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
Mady Mellor95d743c2017-01-10 12:05:27 -080097import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
Beverly8fdb5332019-02-04 14:29:49 -050098import com.android.systemui.plugins.statusbar.StatusBarStateController;
99import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
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) {
Selim Cinek1e059272019-05-21 18:15:00 -0700412 if (mAmbientState.isDarkAtAll()) {
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 */
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700510 private boolean mAnimateBottomOnLayout;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -0400511
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500512 @Inject
513 public NotificationStackScrollLayout(
514 @Named(VIEW_CONTEXT) Context context,
515 AttributeSet attrs,
Lucas Dupin00be88f2019-01-03 17:50:52 -0800516 @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
Selim Cinek3d6ae232019-01-04 14:14:33 -0800517 NotificationRoundnessManager notificationRoundnessManager,
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700518 AmbientPulseManager ambientPulseManager,
Ned Burns9eb06332019-04-23 16:02:12 -0400519 DynamicPrivacyController dynamicPrivacyController,
Ned Burns7eeccdd2019-05-15 14:50:11 -0400520 ActivityStarter activityStarter,
521 StatusBarStateController statusBarStateController) {
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,
Ned Burns7eeccdd2019-05-15 14:50:11 -0400537 statusBarStateController,
Ned Burns9eb06332019-04-23 16:02:12 -0400538 NotificationUtils.useNewInterruptionModel(context));
539 mSectionsManager.inflateViews(context);
Ned Burns61269442019-05-02 18:27:23 -0400540 mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
541 // Leave the shade open if there will be other notifs left over to clear
542 final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
543 clearNotifications(ROWS_GENTLE, closeShade);
544 });
545
Ned Burns9eb06332019-04-23 16:02:12 -0400546 mAmbientState = new AmbientState(context, mSectionsManager);
Lucas Dupin00be88f2019-01-03 17:50:52 -0800547 mRoundnessManager = notificationRoundnessManager;
Selim Cinekd35c2792016-01-21 13:20:57 -0800548 mBgColor = context.getColor(R.color.notification_shade_background_color);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800549 int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
550 int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400551 mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
Selim Cinek1cf41c12014-08-12 20:06:19 +0200552 minHeight, maxHeight);
553 mExpandHelper.setEventSource(this);
554 mExpandHelper.setScrollAdapter(this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400555 mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
556 getContext(), mMenuEventListener);
Muyuan Li333a4fc2016-04-16 17:13:46 -0700557 mStackScrollAlgorithm = createStackScrollAlgorithm(context);
Selim Cinek67b22602014-03-10 15:40:16 +0100558 initView(context);
Dave Mankoffdde5ee62019-05-02 17:36:11 -0400559 mFalsingManager = FalsingManagerFactory.getInstance(context);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800560 mShouldDrawNotificationBackground =
561 res.getBoolean(R.bool.config_drawNotificationBackground);
Anthony Chen7acbb772017-04-07 16:45:25 -0700562 mFadeNotificationsOnDismiss =
563 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
Selim Cinek29aab962018-02-27 17:05:45 -0800564 mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
565 mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800566 addOnExpandedHeightListener(mRoundnessManager::setExpanded);
Lucas Dupin64e2f572019-03-21 14:21:14 -0700567 setOutlineProvider(mOutlineProvider);
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800568
Rohan Shah524cf7b2018-03-15 14:40:02 -0700569 // Blocking helper manager wants to know the expanded state, update as well.
570 NotificationBlockingHelperManager blockingHelperManager =
571 Dependency.get(NotificationBlockingHelperManager.class);
572 addOnExpandedHeightListener((height, unused) -> {
573 blockingHelperManager.setNotificationShadeExpanded(height);
574 });
575
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800576 updateWillNotDraw();
Lucas Dupind285cf02018-01-18 09:18:23 -0800577 mBackgroundPaint.setAntiAlias(true);
Selim Cinek67b22602014-03-10 15:40:16 +0100578 if (DEBUG) {
Selim Cinek67b22602014-03-10 15:40:16 +0100579 mDebugPaint = new Paint();
580 mDebugPaint.setColor(0xffff0000);
581 mDebugPaint.setStrokeWidth(2);
582 mDebugPaint.setStyle(Paint.Style.STROKE);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500583 mDebugPaint.setTextSize(25f);
Selim Cinek67b22602014-03-10 15:40:16 +0100584 }
Jason Monke59dc402018-08-16 12:05:01 -0400585 mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
Gus Prevas33619af2018-10-26 15:40:27 -0400586
587 TunerService tunerService = Dependency.get(TunerService.class);
588 tunerService.addTunable((key, newValue) -> {
589 if (key.equals(LOW_PRIORITY)) {
590 mLowPriorityBeforeSpeedBump = "1".equals(newValue);
Gus Prevasa18dc572019-01-14 16:11:22 -0500591 } else if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) {
592 updateDismissRtlSetting("1".equals(newValue));
Gus Prevas33619af2018-10-26 15:40:27 -0400593 }
Gus Prevasa18dc572019-01-14 16:11:22 -0500594 }, LOW_PRIORITY, Settings.Secure.NOTIFICATION_DISMISS_RTL);
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500595
596 mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
597 @Override
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800598 public void onPostEntryUpdated(NotificationEntry entry) {
Gus Prevas5ecd2b82019-01-04 17:19:26 -0500599 if (!entry.notification.isClearable()) {
600 // The user may have performed a dismiss action on the notification, since it's
601 // not clearable we should snap it back.
602 snapViewIfNeeded(entry);
603 }
604 }
605 });
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700606 dynamicPrivacyController.addListener(this);
Jason Monke59dc402018-08-16 12:05:01 -0400607 }
608
Gus Prevasa18dc572019-01-14 16:11:22 -0500609 private void updateDismissRtlSetting(boolean dismissRtl) {
610 mDismissRtl = dismissRtl;
611 for (int i = 0; i < getChildCount(); i++) {
612 View child = getChildAt(i);
613 if (child instanceof ExpandableNotificationRow) {
614 ((ExpandableNotificationRow) child).setDismissRtl(dismissRtl);
615 }
616 }
617 }
618
Jason Monke59dc402018-08-16 12:05:01 -0400619 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400620 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400621 protected void onFinishInflate() {
622 super.onFinishInflate();
623
624 inflateEmptyShadeView();
625 inflateFooterView();
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400626 mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
Gus Prevas59ec2ff2018-12-28 16:20:28 -0500627 if (mAllowLongPress) {
628 setLongPressListener(mGutsManager::openGuts);
629 }
Jason Monke59dc402018-08-16 12:05:01 -0400630 }
631
Selim Cinek34518f62019-02-28 19:41:18 -0800632 /**
633 * @return the height at which we will wake up when pulsing
634 */
635 public float getPulseHeight() {
Selim Cinek3d6ae232019-01-04 14:14:33 -0800636 ActivatableNotificationView firstChild = getFirstChildWithBackground();
637 if (firstChild != null) {
638 return firstChild.getCollapsedHeight();
639 }
Selim Cinek34518f62019-02-28 19:41:18 -0800640 return 0f;
Selim Cinek3d6ae232019-01-04 14:14:33 -0800641 }
642
Jason Monke59dc402018-08-16 12:05:01 -0400643 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400644 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400645 public void onDensityOrFontScaleChanged() {
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800646 reinflateViews();
647 }
648
649 private void reinflateViews() {
Jason Monke59dc402018-08-16 12:05:01 -0400650 inflateFooterView();
651 inflateEmptyShadeView();
652 updateFooter();
Ned Burns9eb06332019-04-23 16:02:12 -0400653 mSectionsManager.inflateViews(mContext);
Jason Monke59dc402018-08-16 12:05:01 -0400654 }
655
656 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400657 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400658 public void onThemeChanged() {
659 int which;
660 if (mStatusBarState == StatusBarState.KEYGUARD
661 || mStatusBarState == StatusBarState.SHADE_LOCKED) {
662 which = WallpaperManager.FLAG_LOCK;
663 } else {
664 which = WallpaperManager.FLAG_SYSTEM;
665 }
666 final boolean useDarkText = mColorExtractor.getColors(which,
667 true /* ignoreVisibility */).supportsDarkText();
668 updateDecorViews(useDarkText);
669
670 updateFooter();
671 }
672
Fabian Kozynskid254b192019-02-05 13:42:58 -0500673 @Override
674 public void onOverlayChanged() {
675 int newRadius = mContext.getResources().getDimensionPixelSize(
676 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
677 if (mCornerRadius != newRadius) {
678 mCornerRadius = newRadius;
679 invalidate();
680 }
Selim Cinekdd26a7e2019-02-11 18:42:55 -0800681 reinflateViews();
Fabian Kozynskid254b192019-02-05 13:42:58 -0500682 }
683
Jason Monke59dc402018-08-16 12:05:01 -0400684 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400685 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -0400686 public void updateFooter() {
Ned Burns61269442019-05-02 18:27:23 -0400687 boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL);
Jason Monke59dc402018-08-16 12:05:01 -0400688 boolean showFooterView = (showDismissView ||
689 mEntryManager.getNotificationData().getActiveNotifications().size() != 0)
690 && mStatusBarState != StatusBarState.KEYGUARD
691 && !mRemoteInputManager.getController().isRemoteInputActive();
692
693 updateFooterView(showFooterView, showDismissView);
694 }
695
696 /**
697 * Return whether there are any clearable notifications
698 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400699 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -0400700 public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
Jason Monke59dc402018-08-16 12:05:01 -0400701 int childCount = getChildCount();
702 for (int i = 0; i < childCount; i++) {
703 View child = getChildAt(i);
704 if (!(child instanceof ExpandableNotificationRow)) {
705 continue;
706 }
Ned Burns61269442019-05-02 18:27:23 -0400707 final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
708 if (row.canViewBeDismissed() && matchesSelection(row, selection)) {
Jason Monke59dc402018-08-16 12:05:01 -0400709 return true;
710 }
711 }
712 return false;
713 }
714
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400715 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400716 public RemoteInputController.Delegate createDelegate() {
Jason Monke59dc402018-08-16 12:05:01 -0400717 return new RemoteInputController.Delegate() {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500718 public void setRemoteInputActive(NotificationEntry entry,
Jason Monke59dc402018-08-16 12:05:01 -0400719 boolean remoteInputActive) {
720 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
Evan Laird94492852018-10-25 13:43:01 -0400721 entry.notifyHeightChanged(true /* needsAnimation */);
Jason Monke59dc402018-08-16 12:05:01 -0400722 updateFooter();
723 }
724
Ned Burnsf81c4c42019-01-07 14:10:43 -0500725 public void lockScrollTo(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400726 NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
Jason Monke59dc402018-08-16 12:05:01 -0400727 }
728
729 public void requestDisallowLongPressAndDismiss() {
730 requestDisallowLongPress();
731 requestDisallowDismiss();
732 }
733 };
Selim Cinek67b22602014-03-10 15:40:16 +0100734 }
735
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900736 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400737 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400738 protected void onAttachedToWindow() {
739 super.onAttachedToWindow();
Beverly8fdb5332019-02-04 14:29:49 -0500740 ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class))
741 .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
Jason Monke59dc402018-08-16 12:05:01 -0400742 Dependency.get(ConfigurationController.class).addCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400743 }
744
745 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400746 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk1fd3fc32018-08-14 17:20:09 -0400747 protected void onDetachedFromWindow() {
748 super.onDetachedFromWindow();
Jason Monkaf08c152018-12-04 11:12:39 -0500749 Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Jason Monke59dc402018-08-16 12:05:01 -0400750 Dependency.get(ConfigurationController.class).removeCallback(this);
Jason Monk1fd3fc32018-08-14 17:20:09 -0400751 }
752
753 @Override
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -0400754 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -0800755 public NotificationSwipeActionHelper getSwipeActionHelper() {
756 return mSwipeHelper;
757 }
758
Selim Cinek67b22602014-03-10 15:40:16 +0100759 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400760 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupinf03e7522018-06-25 16:21:13 -0700761 public void onUiModeChanged() {
762 mBgColor = mContext.getColor(R.color.notification_shade_background_color);
763 updateBackgroundDimming();
Selim Cinekab9c7b22018-12-11 18:15:47 -0800764 mShelf.onUiModeChanged();
Ned Burns9eb06332019-04-23 16:02:12 -0400765 mSectionsManager.onUiModeChanged();
Lucas Dupinf03e7522018-06-25 16:21:13 -0700766 }
767
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400768 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Selim Cinek67b22602014-03-10 15:40:16 +0100769 protected void onDraw(Canvas canvas) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800770 if (mShouldDrawNotificationBackground
Gus Prevase2d6f042018-10-17 15:25:30 -0400771 && (mSections[0].getCurrentBounds().top
772 < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
Selim Cinekc1d9ab22019-05-21 18:08:30 -0700773 || mAmbientState.isDozing())) {
Lucas Dupind285cf02018-01-18 09:18:23 -0800774 drawBackground(canvas);
Gus Prevas211181532018-12-13 14:49:33 -0500775 } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
776 drawHeadsUpBackground(canvas);
Selim Cinekd381bc32016-08-15 12:40:57 -0700777 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800778
Selim Cinek67b22602014-03-10 15:40:16 +0100779 if (DEBUG) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800780 int y = mTopPadding;
Selim Cinek67b22602014-03-10 15:40:16 +0100781 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Mady Mellor43c2cd12016-12-12 21:05:13 -0800782 y = getLayoutHeight();
Selim Cinek67b22602014-03-10 15:40:16 +0100783 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200784 y = getHeight() - getEmptyBottomMargin();
785 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
Selim Cinek67b22602014-03-10 15:40:16 +0100786 }
787 }
788
Gus Prevas0fa58d62019-01-11 13:58:40 -0500789 @Override
790 public void draw(Canvas canvas) {
791 super.draw(canvas);
792
793 if (DEBUG && ANCHOR_SCROLLING) {
794 if (mScrollAnchorView instanceof ExpandableNotificationRow) {
795 canvas.drawRect(0,
796 mScrollAnchorView.getTranslationY(),
797 getWidth(),
798 mScrollAnchorView.getTranslationY()
799 + ((ExpandableNotificationRow) mScrollAnchorView).getActualHeight(),
800 mDebugPaint);
801 canvas.drawText(Integer.toString(mScrollAnchorViewY), getWidth() - 200,
802 mScrollAnchorView.getTranslationY() + 30, mDebugPaint);
803 int y = (int) mShelf.getTranslationY();
804 canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
805 }
Gus Prevascdc98342019-01-14 14:29:44 -0500806 canvas.drawText(Integer.toString(getMaxNegativeScrollAmount()), getWidth() - 100,
Selim Cinekdb6d6df2019-05-22 20:39:59 -0700807 getTopPadding() + 30, mDebugPaint);
Gus Prevascdc98342019-01-14 14:29:44 -0500808 canvas.drawText(Integer.toString(getMaxPositiveScrollAmount()), getWidth() - 100,
809 getHeight() - 30, mDebugPaint);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500810 }
811 }
812
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400813 @ShadeViewRefactor(RefactorComponent.DECORATOR)
Lucas Dupind285cf02018-01-18 09:18:23 -0800814 private void drawBackground(Canvas canvas) {
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700815 int lockScreenLeft = mSidePaddings;
816 int lockScreenRight = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -0400817 int lockScreenTop = mSections[0].getCurrentBounds().top;
818 int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom;
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700819 int darkLeft = getWidth() / 2;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800820 int darkTop = mTopPadding;
Lucas Dupind285cf02018-01-18 09:18:23 -0800821
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700822 float yProgress = 1 - mInterpolatedDarkAmount;
823 float xProgress = mDarkXInterpolator.getInterpolation(
824 (1 - mLinearDarkAmount) * mBackgroundXFactor);
Lucas Dupin60661a62018-04-12 10:50:13 -0700825
Gus Prevase2d6f042018-10-17 15:25:30 -0400826 int left = (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress);
827 int right = (int) MathUtils.lerp(darkLeft, lockScreenRight, xProgress);
828 int top = (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress);
829 int bottom = (int) MathUtils.lerp(darkTop, lockScreenBottom, yProgress);
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700830 mBackgroundAnimationRect.set(
Gus Prevase2d6f042018-10-17 15:25:30 -0400831 left,
832 top,
833 right,
834 bottom);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200835
Gus Prevase2d6f042018-10-17 15:25:30 -0400836 int backgroundTopAnimationOffset = top - lockScreenTop;
837 // TODO(kprevas): this may not be necessary any more since we don't display the shelf in AOD
838 boolean anySectionHasVisibleChild = false;
839 for (NotificationSection section : mSections) {
840 if (section.getFirstVisibleChild() != null) {
841 anySectionHasVisibleChild = true;
842 break;
843 }
844 }
Selim Cinekc1d9ab22019-05-21 18:08:30 -0700845 if (!mAmbientState.isDozing() || anySectionHasVisibleChild) {
Gus Prevase2d6f042018-10-17 15:25:30 -0400846 drawBackgroundRects(canvas, left, right, top, backgroundTopAnimationOffset);
Lucas Dupind285cf02018-01-18 09:18:23 -0800847 }
Lucas Dupin23a8d3b2018-10-08 20:57:35 -0700848
Lucas Dupin16cfe452018-02-08 13:14:50 -0800849 updateClipping();
Lucas Dupind285cf02018-01-18 09:18:23 -0800850 }
851
Gus Prevase2d6f042018-10-17 15:25:30 -0400852 /**
853 * Draws round rects for each background section.
854 *
855 * We want to draw a round rect for each background section as defined by {@link #mSections}.
856 * However, if two sections are directly adjacent with no gap between them (e.g. on the
857 * lockscreen where the shelf can appear directly below the high priority section, or while
858 * scrolling the shade so that the top of the shelf is right at the bottom of the high priority
859 * section), we don't want to round the adjacent corners.
860 *
861 * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we
862 * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect.
863 * This method tracks the top of each rect we need to draw, then iterates through the visible
864 * sections. If a section is not adjacent to the previous section, we draw the previous rect
865 * behind the sections we've accumulated up to that point, then start a new rect at the top of
866 * the current section. When we're done iterating we will always have one rect left to draw.
867 */
868 private void drawBackgroundRects(Canvas canvas, int left, int right, int top,
869 int animationYOffset) {
870 int backgroundRectTop = top;
871 int lastSectionBottom =
872 mSections[0].getCurrentBounds().bottom + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800873 int previousLeft = left;
874 boolean first = true;
Gus Prevase2d6f042018-10-17 15:25:30 -0400875 for (NotificationSection section : mSections) {
876 if (section.getFirstVisibleChild() == null) {
877 continue;
878 }
879 int sectionTop = section.getCurrentBounds().top + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800880 int ownLeft = Math.min(Math.max(left, section.getCurrentBounds().left), right);
Gus Prevase2d6f042018-10-17 15:25:30 -0400881 // If sections are directly adjacent to each other, we don't want to draw them
882 // as separate roundrects, as the rounded corners right next to each other look
883 // bad.
Selim Cinekae55d832019-02-22 17:43:43 -0800884 if (sectionTop - lastSectionBottom > DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX
885 || (previousLeft != ownLeft && !first)) {
886 canvas.drawRoundRect(ownLeft,
Gus Prevase2d6f042018-10-17 15:25:30 -0400887 backgroundRectTop,
888 right,
889 lastSectionBottom,
890 mCornerRadius, mCornerRadius, mBackgroundPaint);
891 backgroundRectTop = sectionTop;
892 }
Selim Cinekae55d832019-02-22 17:43:43 -0800893 previousLeft = ownLeft;
Gus Prevase2d6f042018-10-17 15:25:30 -0400894 lastSectionBottom =
895 section.getCurrentBounds().bottom + animationYOffset;
Selim Cinekae55d832019-02-22 17:43:43 -0800896 first = false;
Gus Prevase2d6f042018-10-17 15:25:30 -0400897 }
Selim Cinekae55d832019-02-22 17:43:43 -0800898 canvas.drawRoundRect(previousLeft,
Gus Prevase2d6f042018-10-17 15:25:30 -0400899 backgroundRectTop,
900 right,
901 lastSectionBottom,
902 mCornerRadius, mCornerRadius, mBackgroundPaint);
903 }
904
Gus Prevas211181532018-12-13 14:49:33 -0500905 private void drawHeadsUpBackground(Canvas canvas) {
906 int left = mSidePaddings;
907 int right = getWidth() - mSidePaddings;
908
909 float top = getHeight();
910 float bottom = 0;
911 int childCount = getChildCount();
912 for (int i = 0; i < childCount; i++) {
913 View child = getChildAt(i);
914 if (child.getVisibility() != View.GONE
915 && child instanceof ExpandableNotificationRow) {
916 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
shawnlin3a950c32019-05-15 20:06:10 +0800917 if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0
918 && row.getProvider().shouldShowGutsOnSnapOpen()) {
Gus Prevas211181532018-12-13 14:49:33 -0500919 top = Math.min(top, row.getTranslationY());
920 bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
921 }
922 }
923 }
924
925 if (top < bottom) {
926 canvas.drawRoundRect(
927 left, top, right, bottom,
928 mCornerRadius, mCornerRadius, mBackgroundPaint);
929 }
930 }
931
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400932 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd35c2792016-01-21 13:20:57 -0800933 private void updateBackgroundDimming() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800934 // No need to update the background color if it's not being drawn.
935 if (!mShouldDrawNotificationBackground) {
936 return;
937 }
938
Lucas Dupinb561eda2018-04-09 17:25:04 -0700939 float alpha =
940 BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +0200941 alpha *= 1f - mInterpolatedDarkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -0700942 // We need to manually blend in the background color.
943 int scrimColor = mScrimController.getBackgroundColor();
944 int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
945
946 // Interpolate between semi-transparent notification panel background color
947 // and white AOD separator.
Lucas Dupinf03e7522018-06-25 16:21:13 -0700948 float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */,
949 mLinearDarkAmount);
Lucas Dupinb561eda2018-04-09 17:25:04 -0700950 int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
Lucas Dupind285cf02018-01-18 09:18:23 -0800951
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100952 if (mCachedBackgroundColor != color) {
953 mCachedBackgroundColor = color;
954 mBackgroundPaint.setColor(color);
955 invalidate();
956 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800957 }
958
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400959 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +0100960 private void initView(Context context) {
961 mScroller = new OverScroller(getContext());
Selim Cinek67b22602014-03-10 15:40:16 +0100962 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200963 setClipChildren(false);
Selim Cinek67b22602014-03-10 15:40:16 +0100964 final ViewConfiguration configuration = ViewConfiguration.get(context);
965 mTouchSlop = configuration.getScaledTouchSlop();
966 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
967 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +0100968 mOverflingDistance = configuration.getScaledOverflingDistance();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700969
970 Resources res = context.getResources();
971 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
Selim Cinekaf0dc312015-12-15 17:01:44 -0800972 mStackScrollAlgorithm.initView(context);
Selim Cinek281c2022016-10-13 19:14:43 -0700973 mAmbientState.reload(context);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700974 mPaddingBetweenElements = Math.max(1,
975 res.getDimensionPixelSize(R.dimen.notification_divider_height));
976 mIncreasedPaddingBetweenElements =
977 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
978 mMinTopOverScrollToEscape = res.getDimensionPixelSize(
Selim Cinek1408eb52014-06-02 14:45:38 +0200979 R.dimen.min_top_overscroll_to_qs);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800980 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700981 mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
Selim Cinekb95fd182017-12-21 13:03:32 -0800982 mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
Selim Cinek51d21972017-07-19 17:39:20 -0700983 mMinInteractionHeight = res.getDimensionPixelSize(
984 R.dimen.notification_min_interaction_height);
Selim Cinek0fe07392017-11-09 13:26:34 -0800985 mCornerRadius = res.getDimensionPixelSize(
986 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800987 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
988 R.dimen.heads_up_status_bar_padding);
Selim Cineka5eaa602014-05-12 21:27:47 +0200989 }
990
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400991 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekaef92ef2014-06-06 18:06:04 +0200992 private void notifyHeightChangeListener(ExpandableView view) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700993 notifyHeightChangeListener(view, false /* needsAnimation */);
994 }
995
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -0400996 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -0700997 private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
Selim Cinekaef92ef2014-06-06 18:06:04 +0200998 if (mOnHeightChangedListener != null) {
Lucas Dupin60661a62018-04-12 10:50:13 -0700999 mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
Selim Cinekaef92ef2014-06-06 18:06:04 +02001000 }
Selim Cinek67b22602014-03-10 15:40:16 +01001001 }
1002
1003 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001004 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001005 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1006 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Selim Cinekb95fd182017-12-21 13:03:32 -08001007
1008 int width = MeasureSpec.getSize(widthMeasureSpec);
1009 int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
1010 MeasureSpec.getMode(widthMeasureSpec));
Evan Lairdeb7dbd52018-06-28 13:17:25 -04001011 // Don't constrain the height of the children so we know how big they'd like to be
1012 int childHeightSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
1013 MeasureSpec.UNSPECIFIED);
1014
Selim Cinekfa760d42016-05-10 15:50:53 -04001015 // We need to measure all children even the GONE ones, such that the heights are calculated
1016 // correctly as they are used to calculate how many we can fit on the screen.
1017 final int size = getChildCount();
1018 for (int i = 0; i < size; i++) {
Evan Lairdeb7dbd52018-06-28 13:17:25 -04001019 measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -04001020 }
Selim Cinek67b22602014-03-10 15:40:16 +01001021 }
1022
1023 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001024 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001025 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek67b22602014-03-10 15:40:16 +01001026 // we layout all our children centered on the top
1027 float centerX = getWidth() / 2.0f;
1028 for (int i = 0; i < getChildCount(); i++) {
1029 View child = getChildAt(i);
Selim Cinekfa760d42016-05-10 15:50:53 -04001030 // We need to layout all children even the GONE ones, such that the heights are
1031 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinek67b22602014-03-10 15:40:16 +01001032 float width = child.getMeasuredWidth();
1033 float height = child.getMeasuredHeight();
Selim Cinek67b22602014-03-10 15:40:16 +01001034 child.layout((int) (centerX - width / 2.0f),
1035 0,
1036 (int) (centerX + width / 2.0f),
1037 (int) height);
Selim Cinek67b22602014-03-10 15:40:16 +01001038 }
Jorim Jaggi1d480692014-05-20 19:41:58 +02001039 setMaxLayoutHeight(getHeight());
Selim Cinek67b22602014-03-10 15:40:16 +01001040 updateContentHeight();
Selim Cinekf7a14c02014-07-07 14:01:46 +02001041 clampScrollPosition();
Selim Cinek319bdc42014-05-01 23:01:58 +02001042 requestChildrenUpdate();
Selim Cinek614576e2016-01-20 10:54:09 -08001043 updateFirstAndLastBackgroundViews();
Selim Cinekbc243a92016-09-27 16:35:13 -07001044 updateAlgorithmLayoutMinHeight();
Selim Cinek67b22602014-03-10 15:40:16 +01001045 }
1046
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001047 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5bc852a2015-12-21 12:19:09 -08001048 private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
1049 if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
Selim Cineka5e211b2014-08-11 17:35:48 +02001050 mNeedViewResizeAnimation = true;
1051 mNeedsAnimation = true;
1052 }
Selim Cineka5e211b2014-08-11 17:35:48 +02001053 }
1054
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001055 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekdb167372016-11-17 15:41:17 -08001056 public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
1057 mAmbientState.setSpeedBumpIndex(newIndex);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001058 mNoAmbient = noAmbient;
Selim Cinekc27437b2014-05-14 10:23:33 +02001059 }
1060
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001061 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001062 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001063 public void setChildLocationsChangedListener(
1064 NotificationLogger.OnChildLocationsChangedListener listener) {
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001065 mListener = listener;
1066 }
1067
Selim Cineka7d4f822016-12-06 14:34:47 -08001068 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001069 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001070 public boolean isInVisibleLocation(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001071 ExpandableNotificationRow row = entry.getRow();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001072 ExpandableViewState childViewState = row.getViewState();
1073
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001074 if (childViewState == null) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001075 return false;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001076 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +01001077 if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
Selim Cineka7d4f822016-12-06 14:34:47 -08001078 return false;
Christoph Studer12cf9e52014-10-29 17:35:30 +01001079 }
Selim Cineka7d4f822016-12-06 14:34:47 -08001080 if (row.getVisibility() != View.VISIBLE) {
1081 return false;
1082 }
1083 return true;
Christoph Studer6e3eceb2014-04-01 18:40:27 +02001084 }
1085
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001086 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinek67b22602014-03-10 15:40:16 +01001087 private void setMaxLayoutHeight(int maxLayoutHeight) {
1088 mMaxLayoutHeight = maxLayoutHeight;
Selim Cinek9458b192016-10-25 19:02:42 -07001089 mShelf.setMaxLayoutHeight(maxLayoutHeight);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001090 updateAlgorithmHeightAndPadding();
Selim Cinek67b22602014-03-10 15:40:16 +01001091 }
1092
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001093 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001094 private void updateAlgorithmHeightAndPadding() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001095 mAmbientState.setLayoutHeight(getLayoutHeight());
Selim Cinekbc243a92016-09-27 16:35:13 -07001096 updateAlgorithmLayoutMinHeight();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001097 mAmbientState.setTopPadding(mTopPadding);
Selim Cinek67b22602014-03-10 15:40:16 +01001098 }
1099
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001100 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
Selim Cinekbc243a92016-09-27 16:35:13 -07001101 private void updateAlgorithmLayoutMinHeight() {
shawnlinc3457912018-05-15 16:39:56 +08001102 mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001103 ? getLayoutMinHeight() : 0);
Selim Cinekbc243a92016-09-27 16:35:13 -07001104 }
1105
Selim Cinek67b22602014-03-10 15:40:16 +01001106 /**
1107 * Updates the children views according to the stack scroll algorithm. Call this whenever
1108 * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
1109 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001110 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001111 private void updateChildren() {
Selim Cinek3776fe02016-02-04 13:32:43 -08001112 updateScrollStateForAddedChildren();
Selim Cinek727903c2016-12-06 17:28:10 -08001113 mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
1114 ? 0
1115 : mScroller.getCurrVelocity());
Gus Prevas0fa58d62019-01-11 13:58:40 -05001116 if (ANCHOR_SCROLLING) {
1117 mAmbientState.setAnchorViewIndex(indexOfChild(mScrollAnchorView));
1118 mAmbientState.setAnchorViewY(mScrollAnchorViewY);
1119 } else {
1120 mAmbientState.setScrollY(mOwnScrollY);
1121 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05001122 mStackScrollAlgorithm.resetViewStates(mAmbientState);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001123 if (!isCurrentlyAnimating() && !mNeedsAnimation) {
Selim Cinek572bbd42014-04-25 16:43:27 +02001124 applyCurrentState();
Selim Cinek67b22602014-03-10 15:40:16 +01001125 } else {
Selim Cinekf4c19962014-05-01 21:55:31 +02001126 startAnimationToState();
Selim Cinek67b22602014-03-10 15:40:16 +01001127 }
1128 }
1129
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001130 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc383fd02016-10-21 15:31:26 -07001131 private void onPreDrawDuringAnimation() {
Selim Cineka686b2c2016-10-26 13:58:27 -07001132 mShelf.updateAppearance();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001133 updateClippingToTopRoundedCorner();
Selim Cinekc383fd02016-10-21 15:31:26 -07001134 if (!mNeedsAnimation && !mChildrenUpdateRequested) {
1135 updateBackground();
1136 }
Selim Cinekc383fd02016-10-21 15:31:26 -07001137 }
1138
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001139 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001140 private void updateClippingToTopRoundedCorner() {
Arthur Hungc0ef5652018-05-22 14:00:42 +08001141 Float clipStart = (float) mTopPadding
Jason Monke59dc402018-08-16 12:05:01 -04001142 + mStackTranslation
1143 + mAmbientState.getExpandAnimationTopChange();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001144 Float clipEnd = clipStart + mCornerRadius;
1145 boolean first = true;
1146 for (int i = 0; i < getChildCount(); i++) {
1147 ExpandableView child = (ExpandableView) getChildAt(i);
1148 if (child.getVisibility() == GONE) {
1149 continue;
1150 }
1151 float start = child.getTranslationY();
Arthur Hungc0ef5652018-05-22 14:00:42 +08001152 float end = start + child.getActualHeight();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001153 boolean clip = clipStart > start && clipStart < end
1154 || clipEnd >= start && clipEnd <= end;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001155 clip &= !(first && isScrolledToTop());
Selim Cinekeccf4942018-05-30 09:55:36 -07001156 child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
1157 : ExpandableView.NO_ROUNDNESS);
Selim Cinekb0ee18f2017-12-21 16:15:53 -08001158 first = false;
1159 }
1160 }
1161
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001162 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek3776fe02016-02-04 13:32:43 -08001163 private void updateScrollStateForAddedChildren() {
1164 if (mChildrenToAddAnimated.isEmpty()) {
1165 return;
1166 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001167 if (!ANCHOR_SCROLLING) {
1168 for (int i = 0; i < getChildCount(); i++) {
1169 ExpandableView child = (ExpandableView) getChildAt(i);
1170 if (mChildrenToAddAnimated.contains(child)) {
1171 int startingPosition = getPositionInLinearLayout(child);
1172 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
1173 int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
1174 : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
1175 int childHeight = getIntrinsicHeight(child) + padding;
1176 if (startingPosition < mOwnScrollY) {
1177 // This child starts off screen, so let's keep it offscreen to keep the
1178 // others visible
Selim Cinek3776fe02016-02-04 13:32:43 -08001179
Gus Prevas0fa58d62019-01-11 13:58:40 -05001180 setOwnScrollY(mOwnScrollY + childHeight);
1181 }
Selim Cinek3776fe02016-02-04 13:32:43 -08001182 }
1183 }
1184 }
1185 clampScrollPosition();
1186 }
1187
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001188 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001189 private void updateForcedScroll() {
1190 if (mForcedScroll != null && (!mForcedScroll.hasFocus()
1191 || !mForcedScroll.isAttachedToWindow())) {
1192 mForcedScroll = null;
1193 }
1194 if (mForcedScroll != null) {
1195 ExpandableView expandableView = (ExpandableView) mForcedScroll;
1196 int positionInLinearLayout = getPositionInLinearLayout(expandableView);
1197 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
Adrian Roos4a579672016-05-24 16:54:37 -07001198 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Adrian Roos181385c2016-05-05 17:45:44 -04001199
Gus Prevas0fa58d62019-01-11 13:58:40 -05001200 if (ANCHOR_SCROLLING) {
1201 // TODO
1202 } else {
1203 targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
Adrian Roos4a579672016-05-24 16:54:37 -07001204
Gus Prevas0fa58d62019-01-11 13:58:40 -05001205 // Only apply the scroll if we're scrolling the view upwards, or the view is so
1206 // far up that it is not visible anymore.
1207 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1208 setOwnScrollY(targetScroll);
1209 }
Adrian Roos181385c2016-05-05 17:45:44 -04001210 }
1211 }
1212 }
1213
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001214 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek319bdc42014-05-01 23:01:58 +02001215 private void requestChildrenUpdate() {
Selim Cinek1f553cf2014-05-02 12:01:36 +02001216 if (!mChildrenUpdateRequested) {
1217 getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
1218 mChildrenUpdateRequested = true;
1219 invalidate();
1220 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001221 }
1222
Robert Snoeberger168949a2019-04-18 09:39:42 -04001223 /**
1224 * Returns best effort count of visible notifications.
1225 */
1226 public int getVisibleNotificationCount() {
1227 int count = 0;
1228 for (int i = 0; i < getChildCount(); i++) {
1229 final View child = getChildAt(i);
1230 if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
1231 count++;
1232 }
1233 }
1234 return count;
1235 }
1236
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001237 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001238 private boolean isCurrentlyAnimating() {
Selim Cinek572bbd42014-04-25 16:43:27 +02001239 return mStateAnimator.isRunning();
Selim Cinek67b22602014-03-10 15:40:16 +01001240 }
1241
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001242 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekf7a14c02014-07-07 14:01:46 +02001243 private void clampScrollPosition() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001244 if (ANCHOR_SCROLLING) {
1245 // TODO
1246 } else {
1247 int scrollRange = getScrollRange();
1248 if (scrollRange < mOwnScrollY) {
1249 setOwnScrollY(scrollRange);
1250 }
Selim Cinek67b22602014-03-10 15:40:16 +01001251 }
1252 }
1253
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001254 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001255 public int getTopPadding() {
1256 return mTopPadding;
1257 }
1258
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001259 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001260 private void setTopPadding(int topPadding, boolean animate) {
Lucas Dupin00be88f2019-01-03 17:50:52 -08001261 if (mTopPadding != topPadding) {
1262 mTopPadding = topPadding;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001263 updateAlgorithmHeightAndPadding();
1264 updateContentHeight();
Jorim Jaggi75c95042014-05-16 19:09:59 +02001265 if (animate && mAnimationsEnabled && mIsExpanded) {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001266 mTopPaddingNeedsAnimation = true;
Jason Monke59dc402018-08-16 12:05:01 -04001267 mNeedsAnimation = true;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02001268 }
Selim Cinek319bdc42014-05-01 23:01:58 +02001269 requestChildrenUpdate();
Lucas Dupin60661a62018-04-12 10:50:13 -07001270 notifyHeightChangeListener(null, animate);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001271 }
1272 }
1273
1274 /**
Selim Cinekbc243a92016-09-27 16:35:13 -07001275 * Update the height of the panel.
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001276 *
Selim Cinekbc243a92016-09-27 16:35:13 -07001277 * @param height the expanded height of the panel
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001278 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001279 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbc243a92016-09-27 16:35:13 -07001280 public void setExpandedHeight(float height) {
1281 mExpandedHeight = height;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001282 setIsExpanded(height > 0);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001283 int minExpansionHeight = getMinExpansionHeight();
1284 if (height < minExpansionHeight) {
1285 mClipRect.left = 0;
1286 mClipRect.right = getWidth();
1287 mClipRect.top = 0;
1288 mClipRect.bottom = (int) height;
1289 height = minExpansionHeight;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001290 setRequestedClipBounds(mClipRect);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001291 } else {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001292 setRequestedClipBounds(null);
Selim Cinek48ff9b42016-11-09 19:31:51 -08001293 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001294 int stackHeight;
Selim Cinek94c2d822016-07-13 18:50:04 -07001295 float translationY;
1296 float appearEndPosition = getAppearEndPosition();
1297 float appearStartPosition = getAppearStartPosition();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001298 float appearFraction = 1.0f;
shawnlin5be1f7c2018-05-21 20:50:54 +08001299 boolean appearing = height < appearEndPosition;
1300 mAmbientState.setAppearing(appearing);
1301 if (!appearing) {
Selim Cinekbc243a92016-09-27 16:35:13 -07001302 translationY = 0;
shawnlin8e4e92c2018-04-12 18:47:24 +08001303 if (mShouldShowShelfOnly) {
1304 stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
1305 } else if (mQsExpanded) {
1306 int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
1307 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
1308 if (stackStartPosition <= stackEndPosition) {
1309 stackHeight = stackEndPosition;
1310 } else {
1311 stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
1312 stackEndPosition, mQsExpansionFraction);
1313 }
1314 } else {
1315 stackHeight = (int) height;
1316 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001317 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001318 appearFraction = getAppearFraction(height);
Selim Cinek94c2d822016-07-13 18:50:04 -07001319 if (appearFraction >= 0) {
1320 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
1321 appearFraction);
1322 } else {
1323 // This may happen when pushing up a heads up. We linearly push it up from the
1324 // start
1325 translationY = height - appearStartPosition + getExpandTranslationStart();
1326 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001327 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001328 stackHeight =
1329 getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001330 translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
1331 } else {
1332 stackHeight = (int) (height - translationY);
1333 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001334 }
1335 if (stackHeight != mCurrentStackHeight) {
1336 mCurrentStackHeight = stackHeight;
1337 updateAlgorithmHeightAndPadding();
Selim Cinek319bdc42014-05-01 23:01:58 +02001338 requestChildrenUpdate();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +02001339 }
Selim Cinek94c2d822016-07-13 18:50:04 -07001340 setStackTranslation(translationY);
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001341 for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
1342 BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
1343 listener.accept(mExpandedHeight, appearFraction);
1344 }
Selim Cinekcafa87f2016-10-26 17:00:17 -07001345 }
1346
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001347 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001348 private void setRequestedClipBounds(Rect clipRect) {
1349 mRequestedClipBounds = clipRect;
1350 updateClipping();
1351 }
1352
Lucas Dupin60661a62018-04-12 10:50:13 -07001353 /**
1354 * Return the height of the content ignoring the footer.
1355 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001356 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Lucas Dupin60661a62018-04-12 10:50:13 -07001357 public int getIntrinsicContentHeight() {
1358 return mIntrinsicContentHeight;
1359 }
1360
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001361 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcafa87f2016-10-26 17:00:17 -07001362 public void updateClipping() {
1363 boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
1364 && !mHeadsUpAnimatingAway;
Lucas Dupin64e2f572019-03-21 14:21:14 -07001365 boolean clipToOutline = false;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001366 if (mIsClipped != clipped) {
1367 mIsClipped = clipped;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001368 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08001369
Selim Cinek1e059272019-05-21 18:15:00 -07001370 if (!mAmbientPulseManager.hasNotifications() && mAmbientState.isFullyDark()) {
Lucas Dupin4798ea12018-11-05 19:26:24 -08001371 setClipBounds(null);
1372 } else if (mAmbientState.isDarkAtAll()) {
Lucas Dupin64e2f572019-03-21 14:21:14 -07001373 clipToOutline = true;
1374 invalidateOutline();
Lucas Dupin16cfe452018-02-08 13:14:50 -08001375 } else if (clipped) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001376 setClipBounds(mRequestedClipBounds);
1377 } else {
1378 setClipBounds(null);
1379 }
Lucas Dupin64e2f572019-03-21 14:21:14 -07001380
1381 setClipToOutline(clipToOutline);
Selim Cinek94c2d822016-07-13 18:50:04 -07001382 }
1383
1384 /**
1385 * @return The translation at the beginning when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001386 * Measured relative to the resting position.
Selim Cinek94c2d822016-07-13 18:50:04 -07001387 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001388 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001389 private float getExpandTranslationStart() {
Selim Cinek083f2142018-11-06 16:32:23 -08001390 return -mTopPadding + getMinExpansionHeight() - mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001391 }
1392
1393 /**
1394 * @return the position from where the appear transition starts when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001395 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001396 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001397 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001398 private float getAppearStartPosition() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001399 if (isHeadsUpTransition()) {
Gus Prevase2d6f042018-10-17 15:25:30 -04001400 return mHeadsUpInset
1401 + getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
Selim Cinekd127d792016-11-01 19:11:41 -07001402 }
Selim Cinek48ff9b42016-11-09 19:31:51 -08001403 return getMinExpansionHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07001404 }
1405
1406 /**
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001407 * @return the height of the top heads up notification when pinned. This is different from the
Jason Monke59dc402018-08-16 12:05:01 -04001408 * intrinsic height, which also includes whether the notification is system expanded and
1409 * is mainly used when dragging down from a heads up notification.
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001410 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001411 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001412 private int getTopHeadsUpPinnedHeight() {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001413 NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001414 if (topEntry == null) {
1415 return 0;
1416 }
Evan Laird94492852018-10-25 13:43:01 -04001417 ExpandableNotificationRow row = topEntry.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001418 if (row.isChildInGroup()) {
Ned Burnsf81c4c42019-01-07 14:10:43 -05001419 final NotificationEntry groupSummary
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001420 = mGroupManager.getGroupSummary(row.getStatusBarNotification());
1421 if (groupSummary != null) {
Evan Laird94492852018-10-25 13:43:01 -04001422 row = groupSummary.getRow();
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001423 }
1424 }
1425 return row.getPinnedHeadsUpHeight();
1426 }
1427
1428 /**
Selim Cinek94c2d822016-07-13 18:50:04 -07001429 * @return the position from where the appear transition ends when expanding.
Jason Monke59dc402018-08-16 12:05:01 -04001430 * Measured in absolute height.
Selim Cinek94c2d822016-07-13 18:50:04 -07001431 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001432 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001433 private float getAppearEndPosition() {
Selim Cinekaa417da2016-10-27 18:17:08 -07001434 int appearPosition;
Selim Cinek66440cf2017-05-26 13:48:47 -07001435 int notGoneChildCount = getNotGoneChildCount();
Julia Reynolds34f14962018-05-03 12:40:20 +00001436 if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001437 if (isHeadsUpTransition()
Selim Cinekc1d9ab22019-05-21 18:08:30 -07001438 || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDozing())) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09001439 appearPosition = getTopHeadsUpPinnedHeight();
Selim Cinekcde90e52016-12-22 21:01:49 +01001440 } else {
1441 appearPosition = 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001442 if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
1443 appearPosition += mShelf.getIntrinsicHeight();
1444 }
Selim Cinekcde90e52016-12-22 21:01:49 +01001445 }
Selim Cinekaa417da2016-10-27 18:17:08 -07001446 } else {
Selim Cinekcde90e52016-12-22 21:01:49 +01001447 appearPosition = mEmptyShadeView.getHeight();
Selim Cinekaa417da2016-10-27 18:17:08 -07001448 }
1449 return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
Selim Cinek94c2d822016-07-13 18:50:04 -07001450 }
1451
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001452 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001453 private boolean isHeadsUpTransition() {
Gus Prevase2d6f042018-10-17 15:25:30 -04001454 NotificationSection firstVisibleSection = getFirstVisibleSection();
1455 return mTrackingHeadsUp && firstVisibleSection != null
Selim Cinek459aee32019-02-20 11:18:56 -08001456 && firstVisibleSection.getFirstVisibleChild().isAboveShelf();
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001457 }
1458
Selim Cinek94c2d822016-07-13 18:50:04 -07001459 /**
1460 * @param height the height of the panel
1461 * @return the fraction of the appear animation that has been performed
1462 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001463 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek94c2d822016-07-13 18:50:04 -07001464 public float getAppearFraction(float height) {
1465 float appearEndPosition = getAppearEndPosition();
1466 float appearStartPosition = getAppearStartPosition();
1467 return (height - appearStartPosition)
1468 / (appearEndPosition - appearStartPosition);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001469 }
1470
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001471 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001472 public float getStackTranslation() {
1473 return mStackTranslation;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001474 }
1475
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001476 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd2281152015-04-10 14:37:46 -07001477 private void setStackTranslation(float stackTranslation) {
1478 if (stackTranslation != mStackTranslation) {
1479 mStackTranslation = stackTranslation;
1480 mAmbientState.setStackTranslation(stackTranslation);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001481 requestChildrenUpdate();
1482 }
Selim Cinek67b22602014-03-10 15:40:16 +01001483 }
1484
1485 /**
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001486 * 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 +01001487 * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
1488 *
1489 * @return either the layout height or the externally defined height, whichever is smaller
1490 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001491 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek343e6e22014-04-11 21:23:30 +02001492 private int getLayoutHeight() {
Selim Cinek67b22602014-03-10 15:40:16 +01001493 return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
1494 }
1495
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001496 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek816c8e42015-11-19 12:00:45 -08001497 public int getFirstItemMinHeight() {
1498 final ExpandableView firstChild = getFirstChildNotGone();
1499 return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001500 }
1501
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001502 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Jason Monk16ac3772016-02-10 15:39:21 -05001503 public void setQsContainer(ViewGroup qsContainer) {
1504 mQsContainer = qsContainer;
Jorim Jaggi56306252014-07-03 00:40:09 +02001505 }
1506
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001507 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek684a4422015-04-15 16:18:39 -07001508 public static boolean isPinnedHeadsUp(View v) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001509 if (v instanceof ExpandableNotificationRow) {
1510 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinek684a4422015-04-15 16:18:39 -07001511 return row.isHeadsUp() && row.isPinned();
Selim Cineka59ecc32015-04-07 10:51:49 -07001512 }
1513 return false;
1514 }
1515
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001516 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cineka59ecc32015-04-07 10:51:49 -07001517 private boolean isHeadsUp(View v) {
1518 if (v instanceof ExpandableNotificationRow) {
1519 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1520 return row.isHeadsUp();
1521 }
1522 return false;
1523 }
1524
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001525 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001526 public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
1527 getLocationOnScreen(mTempInt2);
1528 float localTouchY = touchY - mTempInt2[1];
1529
1530 ExpandableView closestChild = null;
1531 float minDist = Float.MAX_VALUE;
1532
1533 // find the view closest to the location, accounting for GONE views
1534 final int count = getChildCount();
1535 for (int childIdx = 0; childIdx < count; childIdx++) {
1536 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
1537 if (slidingChild.getVisibility() == GONE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001538 || slidingChild instanceof StackScrollerDecorView) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001539 continue;
1540 }
1541 float childTop = slidingChild.getTranslationY();
1542 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001543 float bottom = childTop + slidingChild.getActualHeight()
1544 - slidingChild.getClipBottomAmount();
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01001545
1546 float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
1547 if (dist < minDist) {
1548 closestChild = slidingChild;
1549 minDist = dist;
1550 }
1551 }
1552 return closestChild;
1553 }
1554
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001555 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
1556 private ExpandableView getChildAtPosition(float touchX, float touchY) {
Selim Cinek34ed7c02017-09-08 15:03:12 -07001557 return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
1558
1559 }
1560
1561 /**
1562 * Get the child at a certain screen location.
1563 *
Jason Monke59dc402018-08-16 12:05:01 -04001564 * @param touchX the x coordinate
1565 * @param touchY the y coordinate
Selim Cinek34ed7c02017-09-08 15:03:12 -07001566 * @param requireMinHeight Whether a minimum height is required for a child to be returned.
1567 * @return the child at the given location.
1568 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001569 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek34ed7c02017-09-08 15:03:12 -07001570 private ExpandableView getChildAtPosition(float touchX, float touchY,
1571 boolean requireMinHeight) {
Selim Cinek67b22602014-03-10 15:40:16 +01001572 // find the view under the pointer, accounting for GONE views
1573 final int count = getChildCount();
1574 for (int childIdx = 0; childIdx < count; childIdx++) {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001575 ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
Selim Cinek51d21972017-07-19 17:39:20 -07001576 if (slidingChild.getVisibility() != VISIBLE
Selim Cinek4fd5dfc2016-01-19 15:16:15 -08001577 || slidingChild instanceof StackScrollerDecorView) {
Selim Cinek67b22602014-03-10 15:40:16 +01001578 continue;
1579 }
Selim Cinek89faff12014-06-19 16:29:04 -07001580 float childTop = slidingChild.getTranslationY();
1581 float top = childTop + slidingChild.getClipTopAmount();
Selim Cineka686b2c2016-10-26 13:58:27 -07001582 float bottom = childTop + slidingChild.getActualHeight()
1583 - slidingChild.getClipBottomAmount();
Jorim Jaggi28f0e592014-08-05 22:03:07 +02001584
1585 // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
1586 // camera affordance).
1587 int left = 0;
1588 int right = getWidth();
Selim Cinek67b22602014-03-10 15:40:16 +01001589
Selim Cinek34ed7c02017-09-08 15:03:12 -07001590 if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
Selim Cinek51d21972017-07-19 17:39:20 -07001591 && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001592 if (slidingChild instanceof ExpandableNotificationRow) {
1593 ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
Ned Burnsf81c4c42019-01-07 14:10:43 -05001594 NotificationEntry entry = row.getEntry();
Selim Cinek131c1e22015-05-11 19:04:49 -07001595 if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
Evan Laird94492852018-10-25 13:43:01 -04001596 && mHeadsUpManager.getTopEntry().getRow() != row
Selim Cinek5bc852a2015-12-21 12:19:09 -08001597 && mGroupManager.getGroupSummary(
Evan Laird94492852018-10-25 13:43:01 -04001598 mHeadsUpManager.getTopEntry().notification)
1599 != entry) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001600 continue;
1601 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001602 return row.getViewAtPosition(touchY - childTop);
1603 }
Selim Cinek67b22602014-03-10 15:40:16 +01001604 return slidingChild;
1605 }
1606 }
1607 return null;
1608 }
1609
Selim Cinek3d6ae232019-01-04 14:14:33 -08001610 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001611 getLocationOnScreen(mTempInt2);
1612 return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001613 }
1614
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001615 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001616 public void setScrollingEnabled(boolean enable) {
1617 mScrollingEnabled = enable;
1618 }
1619
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001620 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04001621 public void lockScrollTo(View v) {
1622 if (mForcedScroll == v) {
1623 return;
1624 }
1625 mForcedScroll = v;
1626 scrollTo(v);
1627 }
1628
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001629 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001630 public boolean scrollTo(View v) {
Adrian Roos5153d4a2016-03-22 10:01:56 -07001631 ExpandableView expandableView = (ExpandableView) v;
Gus Prevas0fa58d62019-01-11 13:58:40 -05001632 if (ANCHOR_SCROLLING) {
1633 // TODO
1634 } else {
1635 int positionInLinearLayout = getPositionInLinearLayout(v);
1636 int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
1637 int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
Ricky Waicd35def2016-05-03 11:07:07 +01001638
Gus Prevas0fa58d62019-01-11 13:58:40 -05001639 // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
1640 // that it is not visible anymore.
1641 if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
1642 mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
1643 mDontReportNextOverScroll = true;
1644 animateScroll();
1645 return true;
1646 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001647 }
Ricky Waicd35def2016-05-03 11:07:07 +01001648 return false;
Adrian Roos5153d4a2016-03-22 10:01:56 -07001649 }
1650
Adrian Roos181385c2016-05-05 17:45:44 -04001651 /**
1652 * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
Jason Monke59dc402018-08-16 12:05:01 -04001653 * the IME.
Adrian Roos181385c2016-05-05 17:45:44 -04001654 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001655 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos181385c2016-05-05 17:45:44 -04001656 private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
1657 return positionInLinearLayout + v.getIntrinsicHeight() +
felkachang529bfe62018-07-04 12:51:44 +08001658 getImeInset() - getHeight()
1659 + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
Adrian Roos181385c2016-05-05 17:45:44 -04001660 }
1661
Adrian Roos5153d4a2016-03-22 10:01:56 -07001662 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001663 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001664 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Selim Cineka424c502016-04-05 13:07:54 -07001665 mBottomInset = insets.getSystemWindowInsetBottom();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001666
Gus Prevas0fa58d62019-01-11 13:58:40 -05001667 if (ANCHOR_SCROLLING) {
1668 // TODO
1669 } else {
1670 int range = getScrollRange();
1671 if (mOwnScrollY > range) {
1672 // HACK: We're repeatedly getting staggered insets here while the IME is
1673 // animating away. To work around that we'll wait until things have settled.
1674 removeCallbacks(mReclamp);
1675 postDelayed(mReclamp, 50);
1676 } else if (mForcedScroll != null) {
1677 // The scroll was requested before we got the actual inset - in case we need
1678 // to scroll up some more do so now.
1679 scrollTo(mForcedScroll);
1680 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001681 }
1682 return insets;
1683 }
1684
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001685 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Adrian Roos5153d4a2016-03-22 10:01:56 -07001686 private Runnable mReclamp = new Runnable() {
1687 @Override
1688 public void run() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001689 if (ANCHOR_SCROLLING) {
1690 // TODO
1691 } else {
1692 int range = getScrollRange();
1693 mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
1694 }
Adrian Roos5153d4a2016-03-22 10:01:56 -07001695 mDontReportNextOverScroll = true;
1696 mDontClampNextScroll = true;
Selim Cinek9212de82017-02-06 16:04:28 -08001697 animateScroll();
Adrian Roos5153d4a2016-03-22 10:01:56 -07001698 }
1699 };
1700
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001701 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
1702 public void setExpandingEnabled(boolean enable) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001703 mExpandHelper.setEnabled(enable);
1704 }
1705
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001706 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02001707 private boolean isScrollingEnabled() {
1708 return mScrollingEnabled;
Selim Cinek67b22602014-03-10 15:40:16 +01001709 }
1710
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001711 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07001712 private boolean onKeyguard() {
Selim Cinek355652a2016-12-07 13:32:12 -08001713 return mStatusBarState == StatusBarState.KEYGUARD;
Selim Cinek19c8c702014-08-25 22:09:19 +02001714 }
1715
Selim Cinek67b22602014-03-10 15:40:16 +01001716 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001717 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01001718 protected void onConfigurationChanged(Configuration newConfig) {
1719 super.onConfigurationChanged(newConfig);
Adrian Roos22af6502018-02-22 16:57:08 +01001720 mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
Selim Cinek67b22602014-03-10 15:40:16 +01001721 float densityScale = getResources().getDisplayMetrics().density;
1722 mSwipeHelper.setDensityScale(densityScale);
1723 float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
1724 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
1725 initView(getContext());
1726 }
1727
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001728 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dan Sandlereceda3d2014-07-21 15:35:01 -04001729 public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
Mady Mellor9c2c4962016-04-05 10:43:08 -07001730 mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
1731 true /* isDismissAll */);
Selim Cinek67b22602014-03-10 15:40:16 +01001732 }
1733
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001734 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001735 private void snapViewIfNeeded(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04001736 ExpandableNotificationRow child = entry.getRow();
dongwan0605.kim30637e42016-03-02 17:16:47 +09001737 boolean animate = mIsExpanded || isPinnedHeadsUp(child);
Mady Mellor95d743c2017-01-10 12:05:27 -08001738 // If the child is showing the notification menu snap to that
Evan Lairde55c6012019-03-13 12:54:37 -04001739 if (child.getProvider() != null) {
1740 float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
1741 mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
1742 }
dongwan0605.kim30637e42016-03-02 17:16:47 +09001743 }
1744
Selim Cinek67b22602014-03-10 15:40:16 +01001745 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001746 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Ned Burnsf81c4c42019-01-07 14:10:43 -05001747 public ViewGroup getViewParentForNotification(NotificationEntry entry) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001748 return this;
1749 }
1750
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001751 /**
1752 * Perform a scroll upwards and adapt the overscroll amounts accordingly
1753 *
1754 * @param deltaY The amount to scroll upwards, has to be positive.
1755 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001756 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001757 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04001758 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001759 private float overScrollUp(int deltaY, int range) {
1760 deltaY = Math.max(deltaY, 0);
1761 float currentTopAmount = getCurrentOverScrollAmount(true);
1762 float newTopAmount = currentTopAmount - deltaY;
1763 if (currentTopAmount > 0) {
1764 setOverScrollAmount(newTopAmount, true /* onTop */,
1765 false /* animate */);
1766 }
1767 // Top overScroll might not grab all scrolling motion,
1768 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001769 if (ANCHOR_SCROLLING) {
1770 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1771 // TODO: once we're recycling this will need to check the adapter position of the child
1772 ExpandableView lastRow = getLastRowNotGone();
Gus Prevascdc98342019-01-14 14:29:44 -05001773 if (lastRow != null && !lastRow.isInShelf()) {
1774 float distanceToMax = Math.max(0, getMaxPositiveScrollAmount());
1775 if (scrollAmount > distanceToMax) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001776 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1777 // We overScroll on the bottom
1778 setOverScrolledPixels(currentBottomPixels + (scrollAmount - distanceToMax),
1779 false /* onTop */,
1780 false /* animate */);
1781 mScrollAnchorViewY -= distanceToMax;
1782 scrollAmount = 0f;
1783 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001784 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001785 return scrollAmount;
1786 } else {
1787 float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
1788 float newScrollY = mOwnScrollY + scrollAmount;
1789 if (newScrollY > range) {
1790 if (!mExpandedInThisMotion) {
1791 float currentBottomPixels = getCurrentOverScrolledPixels(false);
1792 // We overScroll on the bottom
1793 setOverScrolledPixels(currentBottomPixels + newScrollY - range,
1794 false /* onTop */,
1795 false /* animate */);
1796 }
1797 setOwnScrollY(range);
1798 scrollAmount = 0.0f;
1799 }
1800 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001801 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001802 }
1803
1804 /**
1805 * Perform a scroll downward and adapt the overscroll amounts accordingly
1806 *
1807 * @param deltaY The amount to scroll downwards, has to be negative.
1808 * @return The amount of scrolling to be performed by the scroller,
Jason Monke59dc402018-08-16 12:05:01 -04001809 * not handled by the overScroll amount.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001810 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001811 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001812 private float overScrollDown(int deltaY) {
1813 deltaY = Math.min(deltaY, 0);
1814 float currentBottomAmount = getCurrentOverScrollAmount(false);
1815 float newBottomAmount = currentBottomAmount + deltaY;
1816 if (currentBottomAmount > 0) {
1817 setOverScrollAmount(newBottomAmount, false /* onTop */,
1818 false /* animate */);
1819 }
1820 // Bottom overScroll might not grab all scrolling motion,
1821 // we have to scroll as well.
Gus Prevas0fa58d62019-01-11 13:58:40 -05001822 if (ANCHOR_SCROLLING) {
1823 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1824 // TODO: once we're recycling this will need to check the adapter position of the child
1825 ExpandableView firstChild = getFirstChildNotGone();
1826 float top = firstChild.getTranslationY();
1827 float distanceToTop = mScrollAnchorView.getTranslationY() - top - mScrollAnchorViewY;
1828 if (distanceToTop < -scrollAmount) {
1829 float currentTopPixels = getCurrentOverScrolledPixels(true);
1830 // We overScroll on the top
1831 setOverScrolledPixels(currentTopPixels + (-scrollAmount - distanceToTop),
1832 true /* onTop */,
1833 false /* animate */);
1834 mScrollAnchorView = firstChild;
1835 mScrollAnchorViewY = 0;
1836 scrollAmount = 0f;
1837 }
1838 return scrollAmount;
1839 } else {
1840 float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
1841 float newScrollY = mOwnScrollY + scrollAmount;
1842 if (newScrollY < 0) {
1843 float currentTopPixels = getCurrentOverScrolledPixels(true);
1844 // We overScroll on the top
1845 setOverScrolledPixels(currentTopPixels - newScrollY,
1846 true /* onTop */,
1847 false /* animate */);
1848 setOwnScrollY(0);
1849 scrollAmount = 0.0f;
1850 }
1851 return scrollAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001852 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001853 }
1854
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001855 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001856 private void initVelocityTrackerIfNotExists() {
1857 if (mVelocityTracker == null) {
1858 mVelocityTracker = VelocityTracker.obtain();
1859 }
1860 }
1861
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001862 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001863 private void recycleVelocityTracker() {
1864 if (mVelocityTracker != null) {
1865 mVelocityTracker.recycle();
1866 mVelocityTracker = null;
1867 }
1868 }
1869
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001870 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01001871 private void initOrResetVelocityTracker() {
1872 if (mVelocityTracker == null) {
1873 mVelocityTracker = VelocityTracker.obtain();
1874 } else {
1875 mVelocityTracker.clear();
1876 }
1877 }
1878
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001879 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ricky Waicd35def2016-05-03 11:07:07 +01001880 public void setFinishScrollingCallback(Runnable runnable) {
1881 mFinishScrollingCallback = runnable;
1882 }
1883
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04001884 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek9212de82017-02-06 16:04:28 -08001885 private void animateScroll() {
Selim Cinek67b22602014-03-10 15:40:16 +01001886 if (mScroller.computeScrollOffset()) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001887 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001888 int oldY = mLastScrollerY;
1889 int y = mScroller.getCurrY();
1890 int deltaY = y - oldY;
1891 if (deltaY != 0) {
1892 int maxNegativeScrollAmount = getMaxNegativeScrollAmount();
1893 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
1894 if ((maxNegativeScrollAmount < 0 && deltaY < maxNegativeScrollAmount)
1895 || (maxPositiveScrollAmount > 0 && deltaY > maxPositiveScrollAmount)) {
1896 // This frame takes us into overscroll, so set the max overscroll based on
1897 // the current velocity
1898 setMaxOverScrollFromCurrentVelocity();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001899 }
Gus Prevascdc98342019-01-14 14:29:44 -05001900 customOverScrollBy(deltaY, oldY, 0, (int) mMaxOverScroll);
1901 mLastScrollerY = y;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001902 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05001903 } else {
1904 int oldY = mOwnScrollY;
1905 int y = mScroller.getCurrY();
Selim Cinek67b22602014-03-10 15:40:16 +01001906
Gus Prevas0fa58d62019-01-11 13:58:40 -05001907 if (oldY != y) {
1908 int range = getScrollRange();
1909 if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
Gus Prevascdc98342019-01-14 14:29:44 -05001910 // This frame takes us into overscroll, so set the max overscroll based on
1911 // the current velocity
1912 setMaxOverScrollFromCurrentVelocity();
Selim Cinek67b22602014-03-10 15:40:16 +01001913 }
Selim Cinek67b22602014-03-10 15:40:16 +01001914
Gus Prevas0fa58d62019-01-11 13:58:40 -05001915 if (mDontClampNextScroll) {
1916 range = Math.max(range, oldY);
1917 }
1918 customOverScrollBy(y - oldY, oldY, range,
1919 (int) (mMaxOverScroll));
Adrian Roos5153d4a2016-03-22 10:01:56 -07001920 }
Selim Cinek67b22602014-03-10 15:40:16 +01001921 }
1922
Gus Prevascdc98342019-01-14 14:29:44 -05001923 postOnAnimation(mReflingAndAnimateScroll);
Adrian Roos5153d4a2016-03-22 10:01:56 -07001924 } else {
1925 mDontClampNextScroll = false;
Ricky Waicd35def2016-05-03 11:07:07 +01001926 if (mFinishScrollingCallback != null) {
1927 mFinishScrollingCallback.run();
1928 }
Selim Cinek67b22602014-03-10 15:40:16 +01001929 }
1930 }
1931
Gus Prevascdc98342019-01-14 14:29:44 -05001932 private void setMaxOverScrollFromCurrentVelocity() {
1933 float currVelocity = mScroller.getCurrVelocity();
1934 if (currVelocity >= mMinimumVelocity) {
1935 mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
Selim Cinek4195dd02014-05-19 18:16:14 +02001936 }
Gus Prevascdc98342019-01-14 14:29:44 -05001937 }
Selim Cinek4195dd02014-05-19 18:16:14 +02001938
Gus Prevascdc98342019-01-14 14:29:44 -05001939 /**
1940 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
1941 * would cause us to exceed the provided maximum overscroll, springs back instead.
1942 *
1943 * This method performs the determination of whether we're exceeding the overscroll and clamps
1944 * the scroll amount if so. The actual scrolling/overscrolling happens in
1945 * {@link #onCustomOverScrolled(int, boolean)} (absolute scrolling) or
1946 * {@link #onCustomOverScrolledBy(int, boolean)} (anchor scrolling).
1947 *
1948 * @param deltaY The (signed) number of pixels to scroll.
1949 * @param scrollY The current scroll position (absolute scrolling only).
1950 * @param scrollRangeY The maximum allowable scroll position (absolute scrolling only).
1951 * @param maxOverScrollY The current (unsigned) limit on number of pixels to overscroll by.
1952 */
Selim Cinek4195dd02014-05-19 18:16:14 +02001953 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevascdc98342019-01-14 14:29:44 -05001954 private void customOverScrollBy(int deltaY, int scrollY, int scrollRangeY, int maxOverScrollY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05001955 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05001956 boolean clampedY = false;
1957 if (deltaY < 0) {
1958 int maxScrollAmount = getMaxNegativeScrollAmount();
1959 if (maxScrollAmount > Integer.MIN_VALUE) {
1960 maxScrollAmount -= maxOverScrollY;
1961 if (deltaY < maxScrollAmount) {
1962 deltaY = maxScrollAmount;
1963 clampedY = true;
1964 }
1965 }
1966 } else {
1967 int maxScrollAmount = getMaxPositiveScrollAmount();
1968 if (maxScrollAmount < Integer.MAX_VALUE) {
1969 maxScrollAmount += maxOverScrollY;
1970 if (deltaY > maxScrollAmount) {
1971 deltaY = maxScrollAmount;
1972 clampedY = true;
1973 }
1974 }
1975 }
1976 onCustomOverScrolledBy(deltaY, clampedY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05001977 } else {
1978 int newScrollY = scrollY + deltaY;
1979 final int top = -maxOverScrollY;
1980 final int bottom = maxOverScrollY + scrollRangeY;
Selim Cinek4195dd02014-05-19 18:16:14 +02001981
Gus Prevas0fa58d62019-01-11 13:58:40 -05001982 boolean clampedY = false;
1983 if (newScrollY > bottom) {
1984 newScrollY = bottom;
1985 clampedY = true;
1986 } else if (newScrollY < top) {
1987 newScrollY = top;
1988 clampedY = true;
1989 }
Selim Cinek4195dd02014-05-19 18:16:14 +02001990
Gus Prevas0fa58d62019-01-11 13:58:40 -05001991 onCustomOverScrolled(newScrollY, clampedY);
Selim Cinek4195dd02014-05-19 18:16:14 +02001992 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02001993 }
1994
1995 /**
1996 * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
1997 * overscroll effect based on numPixels. By default this will also cancel animations on the
1998 * same overScroll edge.
1999 *
2000 * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
2001 * the rubber-banding logic.
Jason Monke59dc402018-08-16 12:05:01 -04002002 * @param onTop Should the effect be applied on top of the scroller.
2003 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002004 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002005 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002006 public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
Selim Cinekfed1ab62014-06-17 14:10:33 -07002007 setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002008 }
2009
2010 /**
2011 * Set the effective overScroll amount which will be directly reflected in the layout.
2012 * By default this will also cancel animations on the same overScroll edge.
2013 *
Jason Monke59dc402018-08-16 12:05:01 -04002014 * @param amount The amount to overScroll by.
2015 * @param onTop Should the effect be applied on top of the scroller.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002016 * @param animate Should an animation be performed.
2017 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002018
2019 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002020 public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
2021 setOverScrollAmount(amount, onTop, animate, true);
2022 }
2023
2024 /**
2025 * Set the effective overScroll amount which will be directly reflected in the layout.
2026 *
Jason Monke59dc402018-08-16 12:05:01 -04002027 * @param amount The amount to overScroll by.
2028 * @param onTop Should the effect be applied on top of the scroller.
2029 * @param animate Should an animation be performed.
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002030 * @param cancelAnimators Should running animations be cancelled.
2031 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002032 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002033 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
2034 boolean cancelAnimators) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002035 setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
2036 }
2037
2038 /**
2039 * Set the effective overScroll amount which will be directly reflected in the layout.
2040 *
Jason Monke59dc402018-08-16 12:05:01 -04002041 * @param amount The amount to overScroll by.
2042 * @param onTop Should the effect be applied on top of the scroller.
2043 * @param animate Should an animation be performed.
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002044 * @param cancelAnimators Should running animations be cancelled.
Jason Monke59dc402018-08-16 12:05:01 -04002045 * @param isRubberbanded The value which will be passed to
2046 * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002047 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002048 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002049 public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
2050 boolean cancelAnimators, boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002051 if (cancelAnimators) {
2052 mStateAnimator.cancelOverScrollAnimators(onTop);
2053 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002054 setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002055 }
2056
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002057 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002058 private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
2059 boolean isRubberbanded) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002060 amount = Math.max(0, amount);
2061 if (animate) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002062 mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002063 } else {
Selim Cinekfed1ab62014-06-17 14:10:33 -07002064 setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002065 mAmbientState.setOverScrollAmount(amount, onTop);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002066 if (onTop) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002067 notifyOverscrollTopListener(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002068 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002069 requestChildrenUpdate();
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002070 }
2071 }
2072
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002073 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002074 private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02002075 mExpandHelper.onlyObserveMovements(amount > 1.0f);
2076 if (mDontReportNextOverScroll) {
2077 mDontReportNextOverScroll = false;
2078 return;
2079 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02002080 if (mOverscrollTopChangedListener != null) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002081 mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
Jorim Jaggi290600a2014-05-30 17:02:20 +02002082 }
2083 }
2084
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002085 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi290600a2014-05-30 17:02:20 +02002086 public void setOverscrollTopChangedListener(
2087 OnOverscrollTopChangedListener overscrollTopChangedListener) {
2088 mOverscrollTopChangedListener = overscrollTopChangedListener;
2089 }
2090
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002091 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002092 public float getCurrentOverScrollAmount(boolean top) {
2093 return mAmbientState.getOverScrollAmount(top);
2094 }
2095
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002096 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002097 public float getCurrentOverScrolledPixels(boolean top) {
Jason Monke59dc402018-08-16 12:05:01 -04002098 return top ? mOverScrolledTopPixels : mOverScrolledBottomPixels;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002099 }
2100
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002101 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002102 private void setOverScrolledPixels(float amount, boolean onTop) {
2103 if (onTop) {
2104 mOverScrolledTopPixels = amount;
2105 } else {
2106 mOverScrolledBottomPixels = amount;
2107 }
2108 }
2109
Gus Prevascdc98342019-01-14 14:29:44 -05002110 /**
2111 * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
2112 * would cause us to exceed the provided maximum overscroll, springs back instead.
2113 *
2114 * @param deltaY The (signed) number of pixels to scroll.
2115 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2116 * the overscroll limit.
2117 */
2118 private void onCustomOverScrolledBy(int deltaY, boolean clampedY) {
2119 assert ANCHOR_SCROLLING;
Gus Prevas0fa58d62019-01-11 13:58:40 -05002120 mScrollAnchorViewY -= deltaY;
2121 // Treat animating scrolls differently; see #computeScroll() for why.
2122 if (!mScroller.isFinished()) {
Gus Prevascdc98342019-01-14 14:29:44 -05002123 if (clampedY) {
2124 springBack();
2125 } else {
2126 float overScrollTop = getCurrentOverScrollAmount(true /* top */);
2127 if (isScrolledToTop() && mScrollAnchorViewY > 0) {
2128 notifyOverscrollTopListener(mScrollAnchorViewY,
2129 isRubberbanded(true /* onTop */));
2130 } else {
2131 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true /* onTop */));
2132 }
2133 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002134 }
2135 updateScrollAnchor();
2136 updateOnScrollChange();
2137 }
2138
Gus Prevascdc98342019-01-14 14:29:44 -05002139 /**
2140 * Scrolls to the given position, overscrolling if needed. If called during a fling and the
2141 * position exceeds the provided maximum overscroll, springs back instead.
2142 *
2143 * @param scrollY The target scroll position.
2144 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
2145 * the overscroll limit.
2146 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002147 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek9212de82017-02-06 16:04:28 -08002148 private void onCustomOverScrolled(int scrollY, boolean clampedY) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002149 assert !ANCHOR_SCROLLING;
Selim Cinek67b22602014-03-10 15:40:16 +01002150 // Treat animating scrolls differently; see #computeScroll() for why.
2151 if (!mScroller.isFinished()) {
Selim Cinekef406062016-09-29 17:33:13 -07002152 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002153 if (clampedY) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002154 springBack();
2155 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +02002156 float overScrollTop = getCurrentOverScrollAmount(true);
2157 if (mOwnScrollY < 0) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002158 notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002159 } else {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002160 notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
Jorim Jaggi290600a2014-05-30 17:02:20 +02002161 }
Selim Cinek67b22602014-03-10 15:40:16 +01002162 }
Selim Cinek67b22602014-03-10 15:40:16 +01002163 } else {
Selim Cinek9212de82017-02-06 16:04:28 -08002164 setOwnScrollY(scrollY);
Selim Cinek67b22602014-03-10 15:40:16 +01002165 }
2166 }
2167
Gus Prevascdc98342019-01-14 14:29:44 -05002168 /**
2169 * Springs back from an overscroll by stopping the {@link #mScroller} and animating the
2170 * overscroll amount back to zero.
2171 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002172 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002173 private void springBack() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002174 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002175 boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0;
2176 int maxPositiveScrollAmount = getMaxPositiveScrollAmount();
2177 boolean overscrolledBottom = maxPositiveScrollAmount < 0;
2178 if (overScrolledTop || overscrolledBottom) {
2179 float newAmount;
2180 if (overScrolledTop) {
2181 newAmount = mScrollAnchorViewY;
2182 mScrollAnchorViewY = 0;
2183 mDontReportNextOverScroll = true;
2184 } else {
2185 newAmount = -maxPositiveScrollAmount;
2186 mScrollAnchorViewY -= maxPositiveScrollAmount;
2187 }
2188 setOverScrollAmount(newAmount, overScrolledTop, false);
2189 setOverScrollAmount(0.0f, overScrolledTop, true);
2190 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002191 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002192 } else {
2193 int scrollRange = getScrollRange();
2194 boolean overScrolledTop = mOwnScrollY <= 0;
2195 boolean overScrolledBottom = mOwnScrollY >= scrollRange;
2196 if (overScrolledTop || overScrolledBottom) {
2197 boolean onTop;
2198 float newAmount;
2199 if (overScrolledTop) {
2200 onTop = true;
2201 newAmount = -mOwnScrollY;
2202 setOwnScrollY(0);
2203 mDontReportNextOverScroll = true;
2204 } else {
2205 onTop = false;
2206 newAmount = mOwnScrollY - scrollRange;
2207 setOwnScrollY(scrollRange);
2208 }
2209 setOverScrollAmount(newAmount, onTop, false);
2210 setOverScrollAmount(0.0f, onTop, true);
2211 mScroller.forceFinished(true);
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002212 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002213 }
2214 }
2215
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002216 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek67b22602014-03-10 15:40:16 +01002217 private int getScrollRange() {
felkachang529bfe62018-07-04 12:51:44 +08002218 // In current design, it only use the top HUN to treat all of HUNs
2219 // although there are more than one HUNs
2220 int contentHeight = mContentHeight;
2221 if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
2222 contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
2223 }
2224 int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
Selim Cineka424c502016-04-05 13:07:54 -07002225 int imeInset = getImeInset();
felkachang529bfe62018-07-04 12:51:44 +08002226 scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
Selim Cineka424c502016-04-05 13:07:54 -07002227 return scrollRange;
2228 }
2229
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002230 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cineka424c502016-04-05 13:07:54 -07002231 private int getImeInset() {
2232 return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
Selim Cinek67b22602014-03-10 15:40:16 +01002233 }
2234
Selim Cinek343e6e22014-04-11 21:23:30 +02002235 /**
2236 * @return the first child which has visibility unequal to GONE
2237 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002238 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb55386d2015-12-16 17:26:49 -08002239 public ExpandableView getFirstChildNotGone() {
Selim Cinek343e6e22014-04-11 21:23:30 +02002240 int childCount = getChildCount();
2241 for (int i = 0; i < childCount; i++) {
2242 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002243 if (child.getVisibility() != View.GONE && child != mShelf) {
Selim Cinek816c8e42015-11-19 12:00:45 -08002244 return (ExpandableView) child;
Selim Cinek343e6e22014-04-11 21:23:30 +02002245 }
2246 }
2247 return null;
2248 }
2249
Selim Cinek4a1ac842014-05-01 15:51:58 +02002250 /**
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002251 * @return the child before the given view which has visibility unequal to GONE
2252 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002253 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002254 public ExpandableView getViewBeforeView(ExpandableView view) {
2255 ExpandableView previousView = null;
2256 int childCount = getChildCount();
2257 for (int i = 0; i < childCount; i++) {
2258 View child = getChildAt(i);
2259 if (child == view) {
2260 return previousView;
2261 }
2262 if (child.getVisibility() != View.GONE) {
2263 previousView = (ExpandableView) child;
2264 }
2265 }
2266 return null;
2267 }
2268
2269 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002270 * @return The first child which has visibility unequal to GONE which is currently below the
Jason Monke59dc402018-08-16 12:05:01 -04002271 * given translationY or equal to it.
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002272 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002273 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekef8c2252017-02-10 14:52:18 -08002274 private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002275 int childCount = getChildCount();
2276 for (int i = 0; i < childCount; i++) {
2277 View child = getChildAt(i);
Selim Cinekef8c2252017-02-10 14:52:18 -08002278 if (child.getVisibility() == View.GONE) {
2279 continue;
2280 }
2281 float rowTranslation = child.getTranslationY();
2282 if (rowTranslation >= translationY) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002283 return child;
Selim Cinekef8c2252017-02-10 14:52:18 -08002284 } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
2285 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2286 if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
2287 List<ExpandableNotificationRow> notificationChildren =
2288 row.getNotificationChildren();
2289 for (int childIndex = 0; childIndex < notificationChildren.size();
2290 childIndex++) {
2291 ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
2292 if (rowChild.getTranslationY() + rowTranslation >= translationY) {
2293 return rowChild;
2294 }
2295 }
2296 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002297 }
2298 }
2299 return null;
2300 }
2301
2302 /**
Selim Cinek4a1ac842014-05-01 15:51:58 +02002303 * @return the last child which has visibility unequal to GONE
2304 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002305 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002306 public ExpandableView getLastChildNotGone() {
Selim Cinek4a1ac842014-05-01 15:51:58 +02002307 int childCount = getChildCount();
2308 for (int i = childCount - 1; i >= 0; i--) {
2309 View child = getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002310 if (child.getVisibility() != View.GONE && child != mShelf) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002311 return (ExpandableView) child;
Selim Cinek4a1ac842014-05-01 15:51:58 +02002312 }
2313 }
2314 return null;
2315 }
2316
Gus Prevas0fa58d62019-01-11 13:58:40 -05002317 private ExpandableNotificationRow getLastRowNotGone() {
2318 int childCount = getChildCount();
2319 for (int i = childCount - 1; i >= 0; i--) {
2320 View child = getChildAt(i);
2321 if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
2322 return (ExpandableNotificationRow) child;
2323 }
2324 }
2325 return null;
2326 }
2327
Jorim Jaggi069cd032014-05-15 03:09:01 +02002328 /**
2329 * @return the number of children which have visibility unequal to GONE
2330 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002331 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi069cd032014-05-15 03:09:01 +02002332 public int getNotGoneChildCount() {
2333 int childCount = getChildCount();
2334 int count = 0;
2335 for (int i = 0; i < childCount; i++) {
Selim Cinek2cd45df2015-06-09 18:00:07 -07002336 ExpandableView child = (ExpandableView) getChildAt(i);
Selim Cinekdb167372016-11-17 15:41:17 -08002337 if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
Jorim Jaggi069cd032014-05-15 03:09:01 +02002338 count++;
2339 }
2340 }
2341 return count;
2342 }
2343
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002344 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek67b22602014-03-10 15:40:16 +01002345 private void updateContentHeight() {
2346 int height = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002347 float previousPaddingRequest = mPaddingBetweenElements;
2348 float previousPaddingAmount = 0.0f;
Selim Cinekad7fac02016-10-18 17:09:15 -07002349 int numShownItems = 0;
2350 boolean finish = false;
Selim Cinek5040f2e2019-02-14 18:22:42 -08002351 int maxDisplayedNotifications = mMaxDisplayedNotifications;
Adrian Roos7a9551a2017-01-11 12:27:49 -08002352
Selim Cinek67b22602014-03-10 15:40:16 +01002353 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08002354 ExpandableView expandableView = (ExpandableView) getChildAt(i);
Lucas Dupin60661a62018-04-12 10:50:13 -07002355 boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
Selim Cinek281c2022016-10-13 19:14:43 -07002356 if (expandableView.getVisibility() != View.GONE
Lucas Dupin60661a62018-04-12 10:50:13 -07002357 && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
Adrian Roos7d062c42017-03-30 15:11:43 -07002358 boolean limitReached = maxDisplayedNotifications != -1
2359 && numShownItems >= maxDisplayedNotifications;
Selim Cinek5040f2e2019-02-14 18:22:42 -08002360 if (limitReached) {
Selim Cinekad7fac02016-10-18 17:09:15 -07002361 expandableView = mShelf;
2362 finish = true;
2363 }
Selim Cinek42357e02016-02-24 18:48:01 -08002364 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08002365 float padding;
2366 if (increasedPaddingAmount >= 0.0f) {
2367 padding = (int) NotificationUtils.interpolate(
2368 previousPaddingRequest,
2369 mIncreasedPaddingBetweenElements,
2370 increasedPaddingAmount);
2371 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08002372 mPaddingBetweenElements,
2373 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08002374 increasedPaddingAmount);
2375 } else {
2376 int ownPadding = (int) NotificationUtils.interpolate(
2377 0,
2378 mPaddingBetweenElements,
2379 1.0f + increasedPaddingAmount);
2380 if (previousPaddingAmount > 0.0f) {
2381 padding = (int) NotificationUtils.interpolate(
2382 ownPadding,
2383 mIncreasedPaddingBetweenElements,
2384 previousPaddingAmount);
2385 } else {
2386 padding = ownPadding;
2387 }
2388 previousPaddingRequest = ownPadding;
Jorim Jaggid4a57442014-04-10 02:45:55 +02002389 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08002390 if (height != 0) {
2391 height += padding;
2392 }
2393 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08002394 height += expandableView.getIntrinsicHeight();
Selim Cinekad7fac02016-10-18 17:09:15 -07002395 numShownItems++;
2396 if (finish) {
2397 break;
2398 }
Selim Cinek67b22602014-03-10 15:40:16 +01002399 }
2400 }
Lucas Dupin60661a62018-04-12 10:50:13 -07002401 mIntrinsicContentHeight = height;
Selim Cinekf4b04ae2018-06-13 18:23:45 -07002402
Selim Cinekdb6d6df2019-05-22 20:39:59 -07002403 // The topPadding can be bigger than the regular padding when qs is expanded, in that
2404 // state the maxPanelHeight and the contentHeight should be bigger
2405 mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
Selim Cinekc22fff62016-05-20 12:44:30 -07002406 updateScrollability();
Selim Cinek51d21972017-07-19 17:39:20 -07002407 clampScrollPosition();
Selim Cinek91d4cba2016-11-10 19:59:48 -08002408 mAmbientState.setLayoutMaxHeight(mContentHeight);
Selim Cinekc22fff62016-05-20 12:44:30 -07002409 }
2410
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002411 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burnsf81c4c42019-01-07 14:10:43 -05002412 private boolean isPulsing(NotificationEntry entry) {
Selim Cinekebf42342017-07-13 15:46:10 +02002413 return mAmbientState.isPulsing(entry);
Adrian Roos7d062c42017-03-30 15:11:43 -07002414 }
2415
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002416 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002417 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbe2c4432017-05-30 12:11:09 -07002418 public boolean hasPulsingNotifications() {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002419 return mPulsing;
Adrian Roos7d062c42017-03-30 15:11:43 -07002420 }
2421
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002422 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07002423 private void updateScrollability() {
Riddle Hsu065c01c2018-05-10 23:14:19 +08002424 boolean scrollable = !mQsExpanded && getScrollRange() > 0;
Selim Cinekc22fff62016-05-20 12:44:30 -07002425 if (scrollable != mScrollable) {
2426 mScrollable = scrollable;
2427 setFocusable(scrollable);
Selim Cinekef406062016-09-29 17:33:13 -07002428 updateForwardAndBackwardScrollability();
2429 }
2430 }
2431
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002432 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef406062016-09-29 17:33:13 -07002433 private void updateForwardAndBackwardScrollability() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002434 boolean forwardScrollable = mScrollable && !isScrolledToBottom();
2435 boolean backwardsScrollable = mScrollable && !isScrolledToTop();
Selim Cinekef406062016-09-29 17:33:13 -07002436 boolean changed = forwardScrollable != mForwardScrollable
2437 || backwardsScrollable != mBackwardScrollable;
2438 mForwardScrollable = forwardScrollable;
2439 mBackwardScrollable = backwardsScrollable;
2440 if (changed) {
2441 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
Selim Cinekc22fff62016-05-20 12:44:30 -07002442 }
Selim Cinek67b22602014-03-10 15:40:16 +01002443 }
2444
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002445 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002446 private void updateBackground() {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002447 // No need to update the background color if it's not being drawn.
Lucas Dupin16cfe452018-02-08 13:14:50 -08002448 if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
Selim Cinek6811d722016-01-19 17:53:12 -08002449 return;
2450 }
Anthony Chen3cb3ad92016-12-01 10:58:47 -08002451
Selim Cinek6811d722016-01-19 17:53:12 -08002452 updateBackgroundBounds();
Gus Prevase2d6f042018-10-17 15:25:30 -04002453 if (didSectionBoundsChange()) {
2454 boolean animate = mAnimateNextSectionBoundsChange || mAnimateNextBackgroundTop
2455 || mAnimateNextBackgroundBottom || areSectionBoundsAnimating();
Selim Cinek54680902016-10-19 16:49:44 -07002456 if (!isExpanded()) {
2457 abortBackgroundAnimators();
2458 animate = false;
2459 }
2460 if (animate) {
Selim Cinek614576e2016-01-20 10:54:09 -08002461 startBackgroundAnimation();
2462 } else {
Gus Prevase2d6f042018-10-17 15:25:30 -04002463 for (NotificationSection section : mSections) {
2464 section.resetCurrentBounds();
2465 }
Lucas Dupin90a38dd2018-09-05 09:37:37 -07002466 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08002467 }
2468 } else {
Selim Cinek54680902016-10-19 16:49:44 -07002469 abortBackgroundAnimators();
Selim Cinek6811d722016-01-19 17:53:12 -08002470 }
Selim Cinek614576e2016-01-20 10:54:09 -08002471 mAnimateNextBackgroundTop = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04002472 mAnimateNextBackgroundBottom = false;
2473 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08002474 }
2475
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002476 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek54680902016-10-19 16:49:44 -07002477 private void abortBackgroundAnimators() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002478 for (NotificationSection section : mSections) {
2479 section.cancelAnimators();
Selim Cinek54680902016-10-19 16:49:44 -07002480 }
2481 }
2482
Gus Prevase2d6f042018-10-17 15:25:30 -04002483 private boolean didSectionBoundsChange() {
2484 for (NotificationSection section : mSections) {
2485 if (section.didBoundsChange()) {
2486 return true;
2487 }
2488 }
2489 return false;
2490 }
2491
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002492 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Gus Prevase2d6f042018-10-17 15:25:30 -04002493 private boolean areSectionBoundsAnimating() {
2494 for (NotificationSection section : mSections) {
2495 if (section.areBoundsAnimating()) {
2496 return true;
2497 }
2498 }
2499 return false;
Selim Cinek614576e2016-01-20 10:54:09 -08002500 }
2501
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002502 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08002503 private void startBackgroundAnimation() {
Gus Prevase2d6f042018-10-17 15:25:30 -04002504 // TODO(kprevas): do we still need separate fields for top/bottom?
2505 // or can each section manage its own animation state?
2506 NotificationSection firstVisibleSection = getFirstVisibleSection();
2507 NotificationSection lastVisibleSection = getLastVisibleSection();
2508 for (NotificationSection section : mSections) {
2509 section.startBackgroundAnimation(
2510 section == firstVisibleSection
2511 ? mAnimateNextBackgroundTop
2512 : mAnimateNextSectionBoundsChange,
2513 section == lastVisibleSection
2514 ? mAnimateNextBackgroundBottom
2515 : mAnimateNextSectionBoundsChange);
Selim Cinek614576e2016-01-20 10:54:09 -08002516 }
Selim Cinek6811d722016-01-19 17:53:12 -08002517 }
2518
2519 /**
2520 * Update the background bounds to the new desired bounds
2521 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002522 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek6811d722016-01-19 17:53:12 -08002523 private void updateBackgroundBounds() {
shawnlin27716722019-04-17 20:09:45 +08002524 int left = mSidePaddings;
2525 int right = getWidth() - mSidePaddings;
Gus Prevase2d6f042018-10-17 15:25:30 -04002526 for (NotificationSection section : mSections) {
2527 section.getBounds().left = left;
2528 section.getBounds().right = right;
2529 }
shawnlin3a2a2e22018-05-04 17:09:50 +08002530
Selim Cinek614576e2016-01-20 10:54:09 -08002531 if (!mIsExpanded) {
Gus Prevase2d6f042018-10-17 15:25:30 -04002532 for (NotificationSection section : mSections) {
2533 section.getBounds().top = 0;
2534 section.getBounds().bottom = 0;
2535 }
Selim Cinek1791f502016-10-07 17:38:03 -04002536 return;
Selim Cinek614576e2016-01-20 10:54:09 -08002537 }
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002538 int minTopPosition = 0;
Gus Prevase2d6f042018-10-17 15:25:30 -04002539 NotificationSection lastSection = getLastVisibleSection();
Selim Cinek355652a2016-12-07 13:32:12 -08002540 if (mStatusBarState != StatusBarState.KEYGUARD) {
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002541 minTopPosition = (int) (mTopPadding + mStackTranslation);
2542 } else if (lastSection == null) {
2543 minTopPosition = mTopPadding;
Selim Cinek3776fe02016-02-04 13:32:43 -08002544 }
Selim Cinekae55d832019-02-22 17:43:43 -08002545 boolean shiftPulsingWithFirst = mAmbientPulseManager.getAllEntries().count() <= 1;
Selim Cinek3fe7e7e2019-02-15 18:40:53 -08002546 for (NotificationSection section : mSections) {
2547 int minBottomPosition = minTopPosition;
2548 if (section == lastSection) {
2549 // We need to make sure the section goes all the way to the shelf
Selim Cinek3a1d2742019-03-11 18:38:29 -07002550 minBottomPosition = (int) (ViewState.getFinalTranslationY(mShelf)
2551 + mShelf.getIntrinsicHeight());
Gus Prevase83700cb2018-12-14 11:42:51 -05002552 }
Selim Cinekae55d832019-02-22 17:43:43 -08002553 minTopPosition = section.updateBounds(minTopPosition, minBottomPosition,
2554 shiftPulsingWithFirst);
2555 shiftPulsingWithFirst = false;
Gus Prevase83700cb2018-12-14 11:42:51 -05002556 }
Selim Cinek614576e2016-01-20 10:54:09 -08002557 }
2558
Gus Prevase2d6f042018-10-17 15:25:30 -04002559 private NotificationSection getFirstVisibleSection() {
2560 for (NotificationSection section : mSections) {
2561 if (section.getFirstVisibleChild() != null) {
2562 return section;
2563 }
2564 }
2565 return null;
2566 }
2567
2568 private NotificationSection getLastVisibleSection() {
2569 for (int i = mSections.length - 1; i >= 0; i--) {
2570 NotificationSection section = mSections[i];
2571 if (section.getLastVisibleChild() != null) {
2572 return section;
2573 }
2574 }
2575 return null;
2576 }
2577
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002578 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002579 private ActivatableNotificationView getLastChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002580 int childCount = getChildCount();
2581 for (int i = childCount - 1; i >= 0; i--) {
2582 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002583 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2584 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002585 return (ActivatableNotificationView) child;
2586 }
2587 }
2588 return null;
2589 }
2590
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002591 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek614576e2016-01-20 10:54:09 -08002592 private ActivatableNotificationView getFirstChildWithBackground() {
Selim Cinek6811d722016-01-19 17:53:12 -08002593 int childCount = getChildCount();
2594 for (int i = 0; i < childCount; i++) {
2595 View child = getChildAt(i);
Selim Cinek48ff9b42016-11-09 19:31:51 -08002596 if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
2597 && child != mShelf) {
Selim Cinek6811d722016-01-19 17:53:12 -08002598 return (ActivatableNotificationView) child;
2599 }
2600 }
2601 return null;
2602 }
2603
Selim Cinek67b22602014-03-10 15:40:16 +01002604 /**
2605 * Fling the scroll view
2606 *
2607 * @param velocityY The initial velocity in the Y direction. Positive
2608 * numbers mean that the finger/cursor is moving down the screen,
2609 * which means we want to scroll towards the top.
2610 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002611 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Muyuan Li26e30ae2016-04-11 17:31:42 -07002612 protected void fling(int velocityY) {
Selim Cinek67b22602014-03-10 15:40:16 +01002613 if (getChildCount() > 0) {
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002614 float topAmount = getCurrentOverScrollAmount(true);
2615 float bottomAmount = getCurrentOverScrollAmount(false);
2616 if (velocityY < 0 && topAmount > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002617 if (ANCHOR_SCROLLING) {
2618 mScrollAnchorViewY += topAmount;
2619 } else {
2620 setOwnScrollY(mOwnScrollY - (int) topAmount);
2621 }
Selim Cinek1408eb52014-06-02 14:45:38 +02002622 mDontReportNextOverScroll = true;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002623 setOverScrollAmount(0, true, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002624 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002625 * mOverflingDistance + topAmount;
2626 } else if (velocityY > 0 && bottomAmount > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05002627 if (ANCHOR_SCROLLING) {
2628 mScrollAnchorViewY -= bottomAmount;
2629 } else {
2630 setOwnScrollY((int) (mOwnScrollY + bottomAmount));
2631 }
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002632 setOverScrollAmount(0, false, false);
Selim Cinekfed1ab62014-06-17 14:10:33 -07002633 mMaxOverScroll = Math.abs(velocityY) / 1000f
2634 * getRubberBandFactor(false /* onTop */) * mOverflingDistance
Jason Monke59dc402018-08-16 12:05:01 -04002635 + bottomAmount;
Selim Cinek8d9ff9c2014-05-12 15:13:04 +02002636 } else {
2637 // it will be set once we reach the boundary
2638 mMaxOverScroll = 0.0f;
2639 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05002640 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05002641 flingScroller(velocityY);
Gus Prevas0fa58d62019-01-11 13:58:40 -05002642 } else {
2643 int scrollRange = getScrollRange();
2644 int minScrollY = Math.max(0, scrollRange);
2645 if (mExpandedInThisMotion) {
2646 minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
2647 }
2648 mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
2649 mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
Selim Cinek94ab18c2016-02-25 12:35:51 -08002650 }
Selim Cinek67b22602014-03-10 15:40:16 +01002651
Selim Cinek9212de82017-02-06 16:04:28 -08002652 animateScroll();
Selim Cinek67b22602014-03-10 15:40:16 +01002653 }
2654 }
2655
Selim Cinek1408eb52014-06-02 14:45:38 +02002656 /**
Gus Prevascdc98342019-01-14 14:29:44 -05002657 * Flings the overscroller with the given velocity (anchor-based scrolling).
2658 *
2659 * Because anchor-based scrolling can't track the current scroll position, the overscroller is
2660 * always started at startY = 0, and we interpret the positions it computes as relative to the
2661 * start of the scroll.
2662 */
2663 private void flingScroller(int velocityY) {
2664 assert ANCHOR_SCROLLING;
2665 mIsScrollerBoundSet = false;
2666 maybeFlingScroller(velocityY, true /* always fling */);
2667 }
2668
2669 private void maybeFlingScroller(int velocityY, boolean alwaysFling) {
2670 assert ANCHOR_SCROLLING;
2671 // Attempt to determine the maximum amount to scroll before we reach the end.
2672 // If the first view is not materialized (for an upwards scroll) or the last view is either
2673 // not materialized or is pinned to the shade (for a downwards scroll), we don't know this
2674 // amount, so we do an unbounded fling and rely on {@link #maybeReflingScroller()} to update
2675 // the scroller once we approach the start/end of the list.
2676 int minY = Integer.MIN_VALUE;
2677 int maxY = Integer.MAX_VALUE;
2678 if (velocityY < 0) {
2679 minY = getMaxNegativeScrollAmount();
2680 if (minY > Integer.MIN_VALUE) {
2681 mIsScrollerBoundSet = true;
2682 }
2683 } else {
2684 maxY = getMaxPositiveScrollAmount();
2685 if (maxY < Integer.MAX_VALUE) {
2686 mIsScrollerBoundSet = true;
2687 }
2688 }
2689 if (mIsScrollerBoundSet || alwaysFling) {
2690 mLastScrollerY = 0;
2691 // x velocity is set to 1 to avoid overscroller bug
2692 mScroller.fling(0, 0, 1, velocityY, 0, 0, minY, maxY, 0,
2693 mExpandedInThisMotion && !isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2);
2694 }
2695 }
2696
2697 /**
2698 * Returns the maximum number of pixels we can scroll in the positive direction (downwards)
2699 * before reaching the bottom of the list (discounting overscroll).
2700 *
2701 * If the return value is negative then we have overscrolled; this is a transient state which
2702 * should immediately be handled by adjusting the anchor position and adding the extra space to
2703 * the bottom overscroll amount.
2704 *
2705 * If we don't know how many pixels we have left to scroll (because the last row has not been
2706 * materialized, or it's in the shelf so it doesn't have its "natural" position), we return
2707 * {@link Integer#MAX_VALUE}.
2708 */
2709 private int getMaxPositiveScrollAmount() {
2710 assert ANCHOR_SCROLLING;
2711 // TODO: once we're recycling we need to check the adapter position of the last child.
2712 ExpandableNotificationRow lastRow = getLastRowNotGone();
2713 if (mScrollAnchorView != null && lastRow != null && !lastRow.isInShelf()) {
2714 // distance from bottom of last child to bottom of notifications area is:
2715 // distance from bottom of last child
2716 return (int) (lastRow.getTranslationY() + lastRow.getActualHeight()
2717 // to top of anchor view
2718 - mScrollAnchorView.getTranslationY()
2719 // plus distance from anchor view to top of notifications area
2720 + mScrollAnchorViewY
2721 // minus height of notifications area.
2722 - (mMaxLayoutHeight - getIntrinsicPadding() - mFooterView.getActualHeight()));
2723 } else {
2724 return Integer.MAX_VALUE;
2725 }
2726 }
2727
2728 /**
2729 * Returns the maximum number of pixels (as a negative number) we can scroll in the negative
2730 * direction (upwards) before reaching the top of the list (discounting overscroll).
2731 *
2732 * If the return value is positive then we have overscrolled; this is a transient state which
2733 * should immediately be handled by adjusting the anchor position and adding the extra space to
2734 * the top overscroll amount.
2735 *
2736 * If we don't know how many pixels we have left to scroll (because the first row has not been
2737 * materialized), we return {@link Integer#MIN_VALUE}.
2738 */
2739 private int getMaxNegativeScrollAmount() {
2740 assert ANCHOR_SCROLLING;
2741 // TODO: once we're recycling we need to check the adapter position of the first child.
2742 ExpandableView firstChild = getFirstChildNotGone();
2743 if (mScrollAnchorView != null && firstChild != null) {
2744 // distance from top of first child to top of notifications area is:
2745 // distance from top of anchor view
2746 return (int) -(mScrollAnchorView.getTranslationY()
2747 // to top of first child
2748 - firstChild.getTranslationY()
2749 // minus distance from top of anchor view to top of notifications area.
2750 - mScrollAnchorViewY);
2751 } else {
2752 return Integer.MIN_VALUE;
2753 }
2754 }
2755
2756 /**
2757 * During a fling, if we were unable to set the bounds of the fling due to the top/bottom view
2758 * not being materialized or being pinned to the shelf, we need to check on every frame if we're
2759 * able to set the bounds. If we are, we fling the scroller again with the newly computed
2760 * bounds.
2761 */
2762 private void maybeReflingScroller() {
2763 if (!mIsScrollerBoundSet) {
2764 // Because mScroller is a flywheel scroller, we fling with the minimum possible
2765 // velocity to establish direction, so as not to perceptibly affect the velocity.
2766 maybeFlingScroller((int) Math.signum(mScroller.getCurrVelocity()),
2767 false /* alwaysFling */);
2768 }
2769 }
2770
2771 /**
Selim Cinek1408eb52014-06-02 14:45:38 +02002772 * @return Whether a fling performed on the top overscroll edge lead to the expanded
2773 * overScroll view (i.e QS).
2774 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002775 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek1408eb52014-06-02 14:45:38 +02002776 private boolean shouldOverScrollFling(int initialVelocity) {
2777 float topOverScroll = getCurrentOverScrollAmount(true);
2778 return mScrolledToTopOnFirstDown
2779 && !mExpandedInThisMotion
2780 && topOverScroll > mMinTopOverScrollToEscape
2781 && initialVelocity > 0;
2782 }
2783
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002784 /**
2785 * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
2786 * account.
2787 *
Jason Monke59dc402018-08-16 12:05:01 -04002788 * @param qsHeight the top padding imposed by the quick settings panel
2789 * @param animate whether to animate the change
Jorim Jaggi06a0c3a2014-10-29 17:17:21 +01002790 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002791 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekdb6d6df2019-05-22 20:39:59 -07002792 public void updateTopPadding(float qsHeight, boolean animate) {
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 Cinekdb6d6df2019-05-22 20:39:59 -07002800 setTopPadding(topPadding, animate);
Selim Cinekbc243a92016-09-27 16:35:13 -07002801 setExpandedHeight(mExpandedHeight);
Selim Cinek1408eb52014-06-02 14:45:38 +02002802 }
2803
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002804 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
shawnlin8e4e92c2018-04-12 18:47:24 +08002805 public void setMaxTopPadding(int maxTopPadding) {
2806 mMaxTopPadding = maxTopPadding;
2807 }
2808
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002809 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd1ad9ab2016-03-01 17:52:20 -08002810 public int getLayoutMinHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002811 if (isHeadsUpTransition()) {
2812 return getTopHeadsUpPinnedHeight();
2813 }
Anthony Chen9e05d462017-04-07 10:10:21 -07002814 return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
Selim Cinek94c2d822016-07-13 18:50:04 -07002815 }
2816
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002817 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi30c305c2014-07-01 23:34:41 +02002818 public float getTopPaddingOverflow() {
2819 return mTopPaddingOverflow;
2820 }
2821
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002822 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002823 public int getPeekHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -08002824 final ExpandableView firstChild = getFirstChildNotGone();
Selim Cinek567e8452016-03-24 10:54:56 -07002825 final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
Selim Cinek816c8e42015-11-19 12:00:45 -08002826 : mCollapsedSize;
Selim Cinekdb167372016-11-17 15:41:17 -08002827 int shelfHeight = 0;
Gus Prevase2d6f042018-10-17 15:25:30 -04002828 if (getLastVisibleSection() != null && mShelf.getVisibility() != GONE) {
Selim Cinekdb167372016-11-17 15:41:17 -08002829 shelfHeight = mShelf.getIntrinsicHeight();
2830 }
2831 return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02002832 }
2833
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002834 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1408eb52014-06-02 14:45:38 +02002835 private int clampPadding(int desiredPadding) {
2836 return Math.max(desiredPadding, mIntrinsicPadding);
2837 }
2838
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002839 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfed1ab62014-06-17 14:10:33 -07002840 private float getRubberBandFactor(boolean onTop) {
2841 if (!onTop) {
2842 return RUBBER_BAND_FACTOR_NORMAL;
2843 }
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002844 if (mExpandedInThisMotion) {
2845 return RUBBER_BAND_FACTOR_AFTER_EXPAND;
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002846 } else if (mIsExpansionChanging || mPanelTracking) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02002847 return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
2848 } else if (mScrolledToTopOnFirstDown) {
2849 return 1.0f;
2850 }
2851 return RUBBER_BAND_FACTOR_NORMAL;
Selim Cinek1408eb52014-06-02 14:45:38 +02002852 }
2853
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002854 /**
2855 * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
2856 * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
2857 * overscroll view (e.g. expand QS).
2858 */
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04002859 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002860 private boolean isRubberbanded(boolean onTop) {
Jorim Jaggie4b840d2015-06-30 16:19:17 -07002861 return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
Jorim Jaggi475b21d2014-07-01 18:13:24 +02002862 || !mScrolledToTopOnFirstDown;
2863 }
2864
Selim Cinek67b22602014-03-10 15:40:16 +01002865
Selim Cinek67b22602014-03-10 15:40:16 +01002866
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002867 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekef5127e2015-12-21 16:55:58 -08002868 public void setChildTransferInProgress(boolean childTransferInProgress) {
2869 mChildTransferInProgress = childTransferInProgress;
2870 }
2871
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002872 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Christoph Studer068f5922014-04-08 17:43:07 -04002873 @Override
Adam Powell6690d012015-06-17 16:41:56 -07002874 public void onViewRemoved(View child) {
Christoph Studer068f5922014-04-08 17:43:07 -04002875 super.onViewRemoved(child);
Selim Cinekb5605e52015-02-20 18:21:41 +01002876 // we only call our internal methods if this is actually a removal and not just a
2877 // notification which becomes a child notification
Selim Cinekef5127e2015-12-21 16:55:58 -08002878 if (!mChildTransferInProgress) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002879 onViewRemovedInternal((ExpandableView) child, this);
Selim Cinekb5605e52015-02-20 18:21:41 +01002880 }
2881 }
2882
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002883 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09002884 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -05002885 public void cleanUpViewStateForEntry(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -04002886 View child = entry.getRow();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04002887 if (child == mSwipeHelper.getTranslatingParentView()) {
2888 mSwipeHelper.clearTranslatingParentView();
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002889 }
Mady Mellor4c97b0a2017-02-15 11:16:13 -08002890 }
2891
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002892 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002893 private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02002894 if (mChangePositionInProgress) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002895 // This is only a position change, don't do anything special
2896 return;
2897 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002898 child.setOnHeightChangedListener(null);
2899 updateScrollStateForRemovedChild(child);
Selim Cinek2aab2fb2015-04-15 18:47:01 -07002900 boolean animationGenerated = generateRemoveAnimation(child);
Selim Cinekd1395642016-04-28 12:22:42 -07002901 if (animationGenerated) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07002902 if (!mSwipedOutViews.contains(child)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002903 || Math.abs(child.getTranslation()) != child.getWidth()) {
Selim Cineka5703182016-05-11 21:23:16 -04002904 container.addTransientView(child, 0);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002905 child.setTransientContainer(container);
Selim Cinekd1395642016-04-28 12:22:42 -07002906 }
2907 } else {
2908 mSwipedOutViews.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002909 }
Selim Cinekcab4a602014-09-03 14:47:57 +02002910 updateAnimationState(false, child);
Selim Cinekc0f4c012014-08-25 15:45:33 +02002911
Selim Cineke9bad242016-06-15 11:46:37 -07002912 focusNextViewIfFocused(child);
2913 }
2914
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002915 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke9bad242016-06-15 11:46:37 -07002916 private void focusNextViewIfFocused(View view) {
2917 if (view instanceof ExpandableNotificationRow) {
2918 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2919 if (row.shouldRefocusOnDismiss()) {
2920 View nextView = row.getChildAfterViewWhenDismissed();
2921 if (nextView == null) {
2922 View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
2923 nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
2924 ? groupParentWhenDismissed.getTranslationY()
Selim Cinekef8c2252017-02-10 14:52:18 -08002925 : view.getTranslationY(), true /* ignoreChildren */);
Selim Cineke9bad242016-06-15 11:46:37 -07002926 }
2927 if (nextView != null) {
2928 nextView.requestAccessibilityFocus();
2929 }
2930 }
2931 }
2932
Selim Cinekc27437b2014-05-14 10:23:33 +02002933 }
2934
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002935 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01002936 private boolean isChildInGroup(View child) {
2937 return child instanceof ExpandableNotificationRow
2938 && mGroupManager.isChildInGroupWithSummary(
Jason Monke59dc402018-08-16 12:05:01 -04002939 ((ExpandableNotificationRow) child).getStatusBarNotification());
Selim Cinekb5605e52015-02-20 18:21:41 +01002940 }
2941
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002942 /**
2943 * Generate a remove animation for a child view.
2944 *
2945 * @param child The view to generate the remove animation for.
2946 * @return Whether an animation was generated.
2947 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002948 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002949 private boolean generateRemoveAnimation(ExpandableView child) {
Selim Cineke0890e52015-06-17 11:17:08 -07002950 if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002951 mAddedHeadsUpChildren.remove(child);
2952 return false;
2953 }
Selim Cinek0fccc722015-07-29 17:04:36 -07002954 if (isClickedHeadsUp(child)) {
Selim Cinek9dd0d042018-05-14 18:12:42 -07002955 // An animation is already running, add it transiently
Dave Mankoffa4d195d2018-11-16 13:33:27 -05002956 mClearTransientViewsWhenFinished.add(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002957 return true;
2958 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002959 if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
Selim Cinek233241f2015-06-01 06:11:19 -07002960 if (!mChildrenToAddAnimated.contains(child)) {
Selim Cinekf4c19962014-05-01 21:55:31 +02002961 // Generate Animations
2962 mChildrenToRemoveAnimated.add(child);
Jorim Jaggi0dd68812014-05-01 19:17:37 +02002963 mNeedsAnimation = true;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002964 return true;
Selim Cinekf4c19962014-05-01 21:55:31 +02002965 } else {
2966 mChildrenToAddAnimated.remove(child);
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02002967 mFromMoreCardAdditions.remove(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002968 return false;
Selim Cinekf4c19962014-05-01 21:55:31 +02002969 }
Selim Cinek572bbd42014-04-25 16:43:27 +02002970 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02002971 return false;
Selim Cinek572bbd42014-04-25 16:43:27 +02002972 }
2973
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002974 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek0fccc722015-07-29 17:04:36 -07002975 private boolean isClickedHeadsUp(View child) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09002976 return HeadsUpUtil.isClickedHeadsUpNotification(child);
Selim Cinek0fccc722015-07-29 17:04:36 -07002977 }
2978
Selim Cineke0890e52015-06-17 11:17:08 -07002979 /**
2980 * Remove a removed child view from the heads up animations if it was just added there
2981 *
2982 * @return whether any child was removed from the list to animate
2983 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04002984 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineke0890e52015-06-17 11:17:08 -07002985 private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
2986 boolean hasAddEvent = false;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002987 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
2988 ExpandableNotificationRow row = eventPair.first;
Selim Cineke0890e52015-06-17 11:17:08 -07002989 boolean isHeadsUp = eventPair.second;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002990 if (child == row) {
Selim Cineke0890e52015-06-17 11:17:08 -07002991 mTmpList.add(eventPair);
2992 hasAddEvent |= isHeadsUp;
Selim Cinekffa6eb82015-05-21 12:11:12 -07002993 }
2994 }
Selim Cineke0890e52015-06-17 11:17:08 -07002995 if (hasAddEvent) {
2996 // This child was just added lets remove all events.
2997 mHeadsUpChangeAnimations.removeAll(mTmpList);
Jason Monke59dc402018-08-16 12:05:01 -04002998 ((ExpandableNotificationRow) child).setHeadsUpAnimatingAway(false);
Selim Cineke0890e52015-06-17 11:17:08 -07002999 }
3000 mTmpList.clear();
3001 return hasAddEvent;
Selim Cinekffa6eb82015-05-21 12:11:12 -07003002 }
3003
Selim Cinek572bbd42014-04-25 16:43:27 +02003004 /**
Selim Cinekb5605e52015-02-20 18:21:41 +01003005 * @param child the child to query
3006 * @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 -04003007 * not expanded
Selim Cinekb5605e52015-02-20 18:21:41 +01003008 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003009 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003010 private boolean isChildInInvisibleGroup(View child) {
3011 if (child instanceof ExpandableNotificationRow) {
3012 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Ned Burnsf81c4c42019-01-07 14:10:43 -05003013 NotificationEntry groupSummary =
Selim Cinekb5605e52015-02-20 18:21:41 +01003014 mGroupManager.getGroupSummary(row.getStatusBarNotification());
Evan Laird94492852018-10-25 13:43:01 -04003015 if (groupSummary != null && groupSummary.getRow() != row) {
Selim Cinek83bc7832015-10-22 13:26:54 -07003016 return row.getVisibility() == View.INVISIBLE;
Selim Cinekb5605e52015-02-20 18:21:41 +01003017 }
3018 }
3019 return false;
3020 }
3021
3022 /**
Selim Cinek572bbd42014-04-25 16:43:27 +02003023 * Updates the scroll position when a child was removed
3024 *
3025 * @param removedChild the removed child
3026 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003027 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek61633a82016-01-25 15:54:10 -08003028 private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003029 if (ANCHOR_SCROLLING) {
3030 if (removedChild == mScrollAnchorView) {
3031 ExpandableView firstChild = getFirstChildNotGone();
3032 if (firstChild != null) {
3033 mScrollAnchorView = firstChild;
3034 } else {
3035 mScrollAnchorView = mShelf;
3036 }
3037 // Adjust anchor view Y by the distance between the old and new anchors
3038 // so that there's no visible change.
3039 mScrollAnchorViewY +=
3040 mScrollAnchorView.getTranslationY() - removedChild.getTranslationY();
3041 }
3042 updateScrollAnchor();
3043 // TODO: once we're recycling this will need to check the adapter position of the child
3044 if (mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY > 0) {
3045 mScrollAnchorViewY = 0;
3046 }
3047 updateOnScrollChange();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003048 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003049 int startingPosition = getPositionInLinearLayout(removedChild);
3050 float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
3051 int padding;
3052 if (increasedPaddingAmount >= 0) {
3053 padding = (int) NotificationUtils.interpolate(
3054 mPaddingBetweenElements,
3055 mIncreasedPaddingBetweenElements,
3056 increasedPaddingAmount);
3057 } else {
3058 padding = (int) NotificationUtils.interpolate(
3059 0,
3060 mPaddingBetweenElements,
3061 1.0f + increasedPaddingAmount);
3062 }
3063 int childHeight = getIntrinsicHeight(removedChild) + padding;
3064 int endPosition = startingPosition + childHeight;
3065 if (endPosition <= mOwnScrollY) {
3066 // This child is fully scrolled of the top, so we have to deduct its height from the
3067 // scrollPosition
3068 setOwnScrollY(mOwnScrollY - childHeight);
3069 } else if (startingPosition < mOwnScrollY) {
3070 // This child is currently being scrolled into, set the scroll position to the
3071 // start of this child
3072 setOwnScrollY(startingPosition);
3073 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003074 }
3075 }
3076
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003077 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekd7c4e002014-07-04 18:36:42 +02003078 private int getIntrinsicHeight(View view) {
3079 if (view instanceof ExpandableView) {
3080 ExpandableView expandableView = (ExpandableView) view;
3081 return expandableView.getIntrinsicHeight();
3082 }
3083 return view.getHeight();
3084 }
3085
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003086 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek1f624952017-06-08 19:11:50 -07003087 public int getPositionInLinearLayout(View requestedView) {
Adrian Roos4a579672016-05-24 16:54:37 -07003088 ExpandableNotificationRow childInGroup = null;
3089 ExpandableNotificationRow requestedRow = null;
3090 if (isChildInGroup(requestedView)) {
3091 // We're asking for a child in a group. Calculate the position of the parent first,
3092 // then within the parent.
3093 childInGroup = (ExpandableNotificationRow) requestedView;
3094 requestedView = requestedRow = childInGroup.getNotificationParent();
3095 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003096 int position = 0;
Selim Cineka7ed2c12017-01-23 20:47:24 -08003097 float previousPaddingRequest = mPaddingBetweenElements;
3098 float previousPaddingAmount = 0.0f;
Selim Cinek572bbd42014-04-25 16:43:27 +02003099 for (int i = 0; i < getChildCount(); i++) {
Selim Cinek61633a82016-01-25 15:54:10 -08003100 ExpandableView child = (ExpandableView) getChildAt(i);
3101 boolean notGone = child.getVisibility() != View.GONE;
Selim Cinek281c2022016-10-13 19:14:43 -07003102 if (notGone && !child.hasNoContentHeight()) {
Selim Cinek42357e02016-02-24 18:48:01 -08003103 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
Selim Cineka7ed2c12017-01-23 20:47:24 -08003104 float padding;
3105 if (increasedPaddingAmount >= 0.0f) {
3106 padding = (int) NotificationUtils.interpolate(
3107 previousPaddingRequest,
3108 mIncreasedPaddingBetweenElements,
3109 increasedPaddingAmount);
3110 previousPaddingRequest = (int) NotificationUtils.interpolate(
Selim Cinek42357e02016-02-24 18:48:01 -08003111 mPaddingBetweenElements,
3112 mIncreasedPaddingBetweenElements,
Selim Cineka7ed2c12017-01-23 20:47:24 -08003113 increasedPaddingAmount);
3114 } else {
3115 int ownPadding = (int) NotificationUtils.interpolate(
3116 0,
3117 mPaddingBetweenElements,
3118 1.0f + increasedPaddingAmount);
3119 if (previousPaddingAmount > 0.0f) {
3120 padding = (int) NotificationUtils.interpolate(
3121 ownPadding,
3122 mIncreasedPaddingBetweenElements,
3123 previousPaddingAmount);
3124 } else {
3125 padding = ownPadding;
3126 }
3127 previousPaddingRequest = ownPadding;
Selim Cinek61633a82016-01-25 15:54:10 -08003128 }
Selim Cineka7ed2c12017-01-23 20:47:24 -08003129 if (position != 0) {
3130 position += padding;
3131 }
3132 previousPaddingAmount = increasedPaddingAmount;
Selim Cinek61633a82016-01-25 15:54:10 -08003133 }
Adrian Roos4a579672016-05-24 16:54:37 -07003134 if (child == requestedView) {
3135 if (requestedRow != null) {
3136 position += requestedRow.getPositionOfChild(childInGroup);
3137 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003138 return position;
3139 }
Selim Cinek61633a82016-01-25 15:54:10 -08003140 if (notGone) {
Selim Cinekabdc5a02014-09-02 13:46:00 +02003141 position += getIntrinsicHeight(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003142 }
3143 }
3144 return 0;
Selim Cinek1685e632014-04-08 02:27:49 +02003145 }
3146
3147 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003148 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adam Powell6690d012015-06-17 16:41:56 -07003149 public void onViewAdded(View child) {
Selim Cinek1685e632014-04-08 02:27:49 +02003150 super.onViewAdded(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003151 onViewAddedInternal((ExpandableView) child);
Selim Cinekb5605e52015-02-20 18:21:41 +01003152 }
3153
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003154 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek614576e2016-01-20 10:54:09 -08003155 private void updateFirstAndLastBackgroundViews() {
Gus Prevase2d6f042018-10-17 15:25:30 -04003156 NotificationSection firstSection = getFirstVisibleSection();
3157 NotificationSection lastSection = getLastVisibleSection();
Gus Prevasda13cfa2018-11-20 14:58:48 -05003158 ActivatableNotificationView previousFirstChild =
3159 firstSection == null ? null : firstSection.getFirstVisibleChild();
3160 ActivatableNotificationView previousLastChild =
3161 lastSection == null ? null : lastSection.getLastVisibleChild();
Gus Prevase2d6f042018-10-17 15:25:30 -04003162
Selim Cinek614576e2016-01-20 10:54:09 -08003163 ActivatableNotificationView firstChild = getFirstChildWithBackground();
3164 ActivatableNotificationView lastChild = getLastChildWithBackground();
Ned Burns9eb06332019-04-23 16:02:12 -04003165 boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsInSections(
Gus Prevase2d6f042018-10-17 15:25:30 -04003166 mSections[0], mSections[1], firstChild, lastChild);
3167
Selim Cinek614576e2016-01-20 10:54:09 -08003168 if (mAnimationsEnabled && mIsExpanded) {
Gus Prevasda13cfa2018-11-20 14:58:48 -05003169 mAnimateNextBackgroundTop = firstChild != previousFirstChild;
Selim Cinek6f0a62a2019-04-09 18:40:12 -07003170 mAnimateNextBackgroundBottom = lastChild != previousLastChild || mAnimateBottomOnLayout;
Gus Prevase2d6f042018-10-17 15:25:30 -04003171 mAnimateNextSectionBoundsChange = sectionViewsChanged;
Selim Cinek614576e2016-01-20 10:54:09 -08003172 } else {
3173 mAnimateNextBackgroundTop = false;
3174 mAnimateNextBackgroundBottom = false;
Gus Prevase2d6f042018-10-17 15:25:30 -04003175 mAnimateNextSectionBoundsChange = false;
Selim Cinek614576e2016-01-20 10:54:09 -08003176 }
Selim Cinekdb167372016-11-17 15:41:17 -08003177 mAmbientState.setLastVisibleBackgroundChild(lastChild);
Gus Prevase2d6f042018-10-17 15:25:30 -04003178 mRoundnessManager.updateRoundedChildren(mSections);
Selim Cinek6f0a62a2019-04-09 18:40:12 -07003179 mAnimateBottomOnLayout = false;
Selim Cinek515b2032017-11-15 10:20:19 -08003180 invalidate();
Selim Cinek614576e2016-01-20 10:54:09 -08003181 }
3182
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003183 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003184 private void onViewAddedInternal(ExpandableView child) {
Selim Cinekd06c41c2015-07-06 14:51:36 -07003185 updateHideSensitiveForChild(child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003186 child.setOnHeightChangedListener(this);
Jorim Jaggif6411742014-08-05 17:10:43 +00003187 generateAddAnimation(child, false /* fromMoreCard */);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003188 updateAnimationState(child);
Selim Cinek98713a42015-09-21 15:47:20 +02003189 updateChronometerForChild(child);
Gus Prevasa18dc572019-01-14 16:11:22 -05003190 if (child instanceof ExpandableNotificationRow) {
3191 ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
3192 }
Gus Prevas0fa58d62019-01-11 13:58:40 -05003193 if (ANCHOR_SCROLLING) {
3194 // TODO: once we're recycling this will need to check the adapter position of the child
3195 if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) {
3196 // New child was added at the top while we're scrolled to the top;
3197 // make it the new anchor view so that we stay at the top.
3198 mScrollAnchorView = child;
3199 }
3200 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003201 }
3202
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003203 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003204 private void updateHideSensitiveForChild(ExpandableView child) {
3205 child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
Selim Cinekd06c41c2015-07-06 14:51:36 -07003206 }
3207
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003208 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003209 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003210 public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
Selim Cinekd1395642016-04-28 12:22:42 -07003211 onViewRemovedInternal(row, childrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01003212 }
3213
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003214 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003215 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003216 public void notifyGroupChildAdded(ExpandableView row) {
Selim Cinekb5605e52015-02-20 18:21:41 +01003217 onViewAddedInternal(row);
3218 }
3219
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003220 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003221 public void setAnimationsEnabled(boolean animationsEnabled) {
3222 mAnimationsEnabled = animationsEnabled;
Selim Cinekcab4a602014-09-03 14:47:57 +02003223 updateNotificationAnimationStates();
Rohan Shah8ee53652018-04-05 11:13:50 -07003224 if (!animationsEnabled) {
3225 mSwipedOutViews.clear();
3226 mChildrenToRemoveAnimated.clear();
3227 clearTemporaryViewsInGroup(this);
3228 }
Selim Cinekcab4a602014-09-03 14:47:57 +02003229 }
3230
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003231 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003232 private void updateNotificationAnimationStates() {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003233 boolean running = mAnimationsEnabled || hasPulsingNotifications();
Selim Cinek09bd29d2017-02-03 15:30:28 -08003234 mShelf.setAnimationsEnabled(running);
Selim Cinekcab4a602014-09-03 14:47:57 +02003235 int childCount = getChildCount();
3236 for (int i = 0; i < childCount; i++) {
3237 View child = getChildAt(i);
Selim Cinek8d490d42015-04-10 00:05:50 -07003238 running &= mIsExpanded || isPinnedHeadsUp(child);
Selim Cinekcab4a602014-09-03 14:47:57 +02003239 updateAnimationState(running, child);
3240 }
3241 }
3242
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003243 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek51ae05d2014-09-09 15:51:38 +02003244 private void updateAnimationState(View child) {
Selim Cinekbe2c4432017-05-30 12:11:09 -07003245 updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
Selim Cinekcd5b22f2016-03-08 16:15:41 -08003246 && (mIsExpanded || isPinnedHeadsUp(child)), child);
Selim Cinek51ae05d2014-09-09 15:51:38 +02003247 }
3248
Selim Cinek2627d722018-01-19 12:16:49 -08003249 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003250 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek2627d722018-01-19 12:16:49 -08003251 public void setExpandingNotification(ExpandableNotificationRow row) {
3252 mAmbientState.setExpandingNotification(row);
3253 requestChildrenUpdate();
3254 }
3255
3256 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003257 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek8875de12018-03-22 10:14:32 -07003258 public void bindRow(ExpandableNotificationRow row) {
Selim Cinekf0c79e12018-05-14 17:17:31 -07003259 row.setHeadsUpAnimatingAwayListener(animatingAway -> {
3260 mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
3261 mHeadsUpAppearanceController.updateHeader(row.getEntry());
3262 });
Selim Cinek8875de12018-03-22 10:14:32 -07003263 }
3264
3265 @Override
Selim Cinekfdf80332019-03-07 17:29:55 -08003266 public boolean containsView(View v) {
3267 return v.getParent() == this;
3268 }
3269
3270 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003271 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek2627d722018-01-19 12:16:49 -08003272 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
3273 mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
3274 requestChildrenUpdate();
3275 }
Selim Cinek51ae05d2014-09-09 15:51:38 +02003276
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003277 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekcab4a602014-09-03 14:47:57 +02003278 private void updateAnimationState(boolean running, View child) {
3279 if (child instanceof ExpandableNotificationRow) {
3280 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3281 row.setIconAnimationRunning(running);
3282 }
Jorim Jaggi75c95042014-05-16 19:09:59 +02003283 }
3284
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003285 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi75c95042014-05-16 19:09:59 +02003286 public boolean isAddOrRemoveAnimationPending() {
3287 return mNeedsAnimation
3288 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
3289 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003290
3291 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003292 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003293 public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003294 if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003295 // Generate Animations
3296 mChildrenToAddAnimated.add(child);
Jorim Jaggif6411742014-08-05 17:10:43 +00003297 if (fromMoreCard) {
3298 mFromMoreCardAdditions.add(child);
3299 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003300 mNeedsAnimation = true;
Selim Cinek572bbd42014-04-25 16:43:27 +02003301 }
Selim Cinekf306d9b2017-02-21 11:45:13 -08003302 if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003303 mAddedHeadsUpChildren.add(child);
3304 mChildrenToAddAnimated.remove(child);
3305 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003306 }
3307
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09003308 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003309 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003310 public void changeViewPosition(ExpandableView child, int newIndex) {
Dan Sandlereceda3d2014-07-21 15:35:01 -04003311 int currentIndex = indexOfChild(child);
Rohan Shah8ee53652018-04-05 11:13:50 -07003312
3313 if (currentIndex == -1) {
3314 boolean isTransient = false;
3315 if (child instanceof ExpandableNotificationRow
Jason Monke59dc402018-08-16 12:05:01 -04003316 && ((ExpandableNotificationRow) child).getTransientContainer() != null) {
Rohan Shah8ee53652018-04-05 11:13:50 -07003317 isTransient = true;
3318 }
3319 Log.e(TAG, "Attempting to re-position "
3320 + (isTransient ? "transient" : "")
3321 + " view {"
3322 + child
3323 + "}");
3324 return;
3325 }
3326
Dan Sandlereceda3d2014-07-21 15:35:01 -04003327 if (child != null && child.getParent() == this && currentIndex != newIndex) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003328 mChangePositionInProgress = true;
Jason Monke59dc402018-08-16 12:05:01 -04003329 ((ExpandableView) child).setChangingPosition(true);
Selim Cinekc27437b2014-05-14 10:23:33 +02003330 removeView(child);
3331 addView(child, newIndex);
Jason Monke59dc402018-08-16 12:05:01 -04003332 ((ExpandableView) child).setChangingPosition(false);
Selim Cinek159ffdb2014-06-04 22:24:18 +02003333 mChangePositionInProgress = false;
Dan Sandlereceda3d2014-07-21 15:35:01 -04003334 if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
Selim Cinek159ffdb2014-06-04 22:24:18 +02003335 mChildrenChangingPositions.add(child);
3336 mNeedsAnimation = true;
3337 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003338 }
3339 }
3340
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003341 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekf4c19962014-05-01 21:55:31 +02003342 private void startAnimationToState() {
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003343 if (mNeedsAnimation) {
Rohan Shahb9d500a2018-06-25 16:27:16 -07003344 generateAllAnimationEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003345 mNeedsAnimation = false;
Selim Cinek572bbd42014-04-25 16:43:27 +02003346 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003347 if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
Selim Cinekea66ca02016-05-24 13:33:47 -07003348 setAnimationRunning(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003349 mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003350 mAnimationEvents.clear();
Selim Cinek6811d722016-01-19 17:53:12 -08003351 updateBackground();
Selim Cinek33223572016-02-19 19:32:22 -08003352 updateViewShadows();
Selim Cinekb0ee18f2017-12-21 16:15:53 -08003353 updateClippingToTopRoundedCorner();
Selim Cinekf4c19962014-05-01 21:55:31 +02003354 } else {
3355 applyCurrentState();
3356 }
Jorim Jaggidbc3dce2014-08-01 01:16:36 +02003357 mGoToFullShadeDelay = 0;
Selim Cinek572bbd42014-04-25 16:43:27 +02003358 }
3359
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003360 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shahb9d500a2018-06-25 16:27:16 -07003361 private void generateAllAnimationEvents() {
Selim Cineka59ecc32015-04-07 10:51:49 -07003362 generateHeadsUpAnimationEvents();
Selim Cinek572bbd42014-04-25 16:43:27 +02003363 generateChildRemovalEvents();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003364 generateChildAdditionEvents();
3365 generatePositionChangeEvents();
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003366 generateTopPaddingEvent();
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003367 generateActivateEvent();
3368 generateDimmedEvent();
Jorim Jaggiae441282014-08-01 02:45:18 +02003369 generateHideSensitiveEvent();
John Spurlockbf370992014-06-17 13:58:31 -04003370 generateDarkEvent();
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003371 generateGoToFullShadeEvent();
Selim Cineka5e211b2014-08-11 17:35:48 +02003372 generateViewResizeEvent();
Selim Cinekb5605e52015-02-20 18:21:41 +01003373 generateGroupExpansionEvent();
Selim Cinekd9acca52014-09-01 22:33:25 +02003374 generateAnimateEverythingEvent();
Selim Cinek572bbd42014-04-25 16:43:27 +02003375 }
3376
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003377 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003378 private void generateHeadsUpAnimationEvents() {
3379 for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003380 ExpandableNotificationRow row = eventPair.first;
3381 boolean isHeadsUp = eventPair.second;
3382 int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
3383 boolean onBottom = false;
Selim Cinek131c1e22015-05-11 19:04:49 -07003384 boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
Selim Cinekaac93252015-04-14 20:04:12 -07003385 if (!mIsExpanded && !isHeadsUp) {
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07003386 type = row.wasJustClicked()
3387 ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
3388 : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
Selim Cinek76e813c2016-07-14 11:16:58 -07003389 if (row.isChildInGroup()) {
3390 // We can otherwise get stuck in there if it was just isolated
Selim Cinekcafa87f2016-10-26 17:00:17 -07003391 row.setHeadsUpAnimatingAway(false);
Selim Cinekf93bf3e2018-05-08 14:43:21 -07003392 continue;
Selim Cinek76e813c2016-07-14 11:16:58 -07003393 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003394 } else {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003395 ExpandableViewState viewState = row.getViewState();
Selim Cinekeaee9c02015-06-25 11:04:20 -04003396 if (viewState == null) {
3397 // A view state was never generated for this view, so we don't need to animate
3398 // this. This may happen with notification children.
3399 continue;
Selim Cineka59ecc32015-04-07 10:51:49 -07003400 }
Selim Cinekeaee9c02015-06-25 11:04:20 -04003401 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
3402 if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
3403 // Our custom add animation
3404 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
3405 } else {
3406 // Normal add animation
3407 type = AnimationEvent.ANIMATION_TYPE_ADD;
3408 }
3409 onBottom = !pinnedAndClosed;
3410 }
Selim Cineka59ecc32015-04-07 10:51:49 -07003411 }
3412 AnimationEvent event = new AnimationEvent(row, type);
3413 event.headsUpFromBottom = onBottom;
3414 mAnimationEvents.add(event);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003415 }
3416 mHeadsUpChangeAnimations.clear();
Selim Cineka59ecc32015-04-07 10:51:49 -07003417 mAddedHeadsUpChildren.clear();
3418 }
3419
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003420 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekbbcebde2016-11-09 18:28:20 -08003421 private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
Selim Cineka59ecc32015-04-07 10:51:49 -07003422 if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
3423 return false;
3424 }
3425 return true;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07003426 }
3427
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003428 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb5605e52015-02-20 18:21:41 +01003429 private void generateGroupExpansionEvent() {
3430 // Generate a group expansion/collapsing event if there is such a group at all
3431 if (mExpandedGroupView != null) {
3432 mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
3433 AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
3434 mExpandedGroupView = null;
3435 }
3436 }
3437
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003438 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cineka5e211b2014-08-11 17:35:48 +02003439 private void generateViewResizeEvent() {
3440 if (mNeedViewResizeAnimation) {
felkachangd7835b02018-07-17 18:18:13 +08003441 boolean hasDisappearAnimation = false;
3442 for (AnimationEvent animationEvent : mAnimationEvents) {
3443 final int type = animationEvent.animationType;
3444 if (type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
Jason Monke59dc402018-08-16 12:05:01 -04003445 || type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
felkachangd7835b02018-07-17 18:18:13 +08003446 hasDisappearAnimation = true;
3447 break;
3448 }
3449 }
3450
3451 if (!hasDisappearAnimation) {
3452 mAnimationEvents.add(
3453 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
3454 }
Selim Cineka5e211b2014-08-11 17:35:48 +02003455 }
3456 mNeedViewResizeAnimation = false;
3457 }
3458
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003459 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003460 private void generateChildRemovalEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003461 for (ExpandableView child : mChildrenToRemoveAnimated) {
Selim Cinek572bbd42014-04-25 16:43:27 +02003462 boolean childWasSwipedOut = mSwipedOutViews.contains(child);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003463
3464 // we need to know the view after this one
Selim Cinekef8c2252017-02-10 14:52:18 -08003465 float removedTranslation = child.getTranslationY();
3466 boolean ignoreChildren = true;
3467 if (child instanceof ExpandableNotificationRow) {
3468 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
3469 if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
3470 removedTranslation = row.getTranslationWhenRemoved();
3471 ignoreChildren = false;
3472 }
Selim Cinek51052042017-07-04 12:07:55 +02003473 childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
Selim Cinekef8c2252017-02-10 14:52:18 -08003474 }
Selim Cinek51052042017-07-04 12:07:55 +02003475 if (!childWasSwipedOut) {
3476 Rect clipBounds = child.getClipBounds();
Selim Cineke5832ee2017-11-06 14:46:48 -08003477 childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
Rohan Shaha7594962018-05-22 10:59:30 -07003478
3479 if (childWasSwipedOut && child instanceof ExpandableView) {
3480 // Clean up any potential transient views if the child has already been swiped
3481 // out, as we won't be animating it further (due to its height already being
3482 // clipped to 0.
3483 ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
3484 if (transientContainer != null) {
3485 transientContainer.removeTransientView(child);
3486 }
3487 }
Selim Cinek51052042017-07-04 12:07:55 +02003488 }
3489 int animationType = childWasSwipedOut
3490 ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
3491 : AnimationEvent.ANIMATION_TYPE_REMOVE;
3492 AnimationEvent event = new AnimationEvent(child, animationType);
Selim Cinekef8c2252017-02-10 14:52:18 -08003493 event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
3494 ignoreChildren);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003495 mAnimationEvents.add(event);
Selim Cinekd1395642016-04-28 12:22:42 -07003496 mSwipedOutViews.remove(child);
Selim Cinek572bbd42014-04-25 16:43:27 +02003497 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003498 mChildrenToRemoveAnimated.clear();
3499 }
3500
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003501 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003502 private void generatePositionChangeEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003503 for (ExpandableView child : mChildrenChangingPositions) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003504 mAnimationEvents.add(new AnimationEvent(child,
3505 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3506 }
3507 mChildrenChangingPositions.clear();
Selim Cinekb5605e52015-02-20 18:21:41 +01003508 if (mGenerateChildOrderChangedEvent) {
3509 mAnimationEvents.add(new AnimationEvent(null,
3510 AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
3511 mGenerateChildOrderChangedEvent = false;
3512 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02003513 }
3514
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003515 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek572bbd42014-04-25 16:43:27 +02003516 private void generateChildAdditionEvents() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003517 for (ExpandableView child : mChildrenToAddAnimated) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003518 if (mFromMoreCardAdditions.contains(child)) {
3519 mAnimationEvents.add(new AnimationEvent(child,
3520 AnimationEvent.ANIMATION_TYPE_ADD,
3521 StackStateAnimator.ANIMATION_DURATION_STANDARD));
3522 } else {
3523 mAnimationEvents.add(new AnimationEvent(child,
3524 AnimationEvent.ANIMATION_TYPE_ADD));
3525 }
Selim Cinek572bbd42014-04-25 16:43:27 +02003526 }
3527 mChildrenToAddAnimated.clear();
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02003528 mFromMoreCardAdditions.clear();
Christoph Studer068f5922014-04-08 17:43:07 -04003529 }
3530
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003531 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003532 private void generateTopPaddingEvent() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003533 if (mTopPaddingNeedsAnimation) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003534 AnimationEvent event;
Selim Cinekc1d9ab22019-05-21 18:08:30 -07003535 if (mAmbientState.isDozing()) {
Lucas Dupinc445dcc2018-05-07 16:00:11 -07003536 event = new AnimationEvent(null /* view */,
3537 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
3538 KeyguardSliceView.DEFAULT_ANIM_DURATION);
3539 } else {
3540 event = new AnimationEvent(null /* view */,
3541 AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
3542 }
3543 mAnimationEvents.add(event);
Jorim Jaggi98fb09c2014-05-01 22:40:56 +02003544 }
Jorim Jaggi0dd68812014-05-01 19:17:37 +02003545 mTopPaddingNeedsAnimation = false;
3546 }
3547
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003548 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003549 private void generateActivateEvent() {
3550 if (mActivateNeedsAnimation) {
3551 mAnimationEvents.add(
3552 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
3553 }
3554 mActivateNeedsAnimation = false;
3555 }
3556
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003557 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekd9acca52014-09-01 22:33:25 +02003558 private void generateAnimateEverythingEvent() {
3559 if (mEverythingNeedsAnimation) {
3560 mAnimationEvents.add(
3561 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
3562 }
3563 mEverythingNeedsAnimation = false;
3564 }
3565
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003566 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggid552d9d2014-05-07 19:41:13 +02003567 private void generateDimmedEvent() {
3568 if (mDimmedNeedsAnimation) {
3569 mAnimationEvents.add(
3570 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
3571 }
3572 mDimmedNeedsAnimation = false;
3573 }
3574
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003575 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggiae441282014-08-01 02:45:18 +02003576 private void generateHideSensitiveEvent() {
3577 if (mHideSensitiveNeedsAnimation) {
3578 mAnimationEvents.add(
3579 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
3580 }
3581 mHideSensitiveNeedsAnimation = false;
3582 }
3583
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003584 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
John Spurlockbf370992014-06-17 13:58:31 -04003585 private void generateDarkEvent() {
3586 if (mDarkNeedsAnimation) {
Adrian Roos28f90c72017-05-08 17:24:26 -07003587 AnimationEvent ev = new AnimationEvent(null,
3588 AnimationEvent.ANIMATION_TYPE_DARK,
3589 new AnimationFilter()
3590 .animateDark()
3591 .animateY(mShelf));
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01003592 ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
3593 mAnimationEvents.add(ev);
John Spurlockbf370992014-06-17 13:58:31 -04003594 }
3595 mDarkNeedsAnimation = false;
3596 }
3597
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04003598 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi60d07c52014-07-31 15:38:21 +02003599 private void generateGoToFullShadeEvent() {
3600 if (mGoToFullShadeNeedsAnimation) {
3601 mAnimationEvents.add(
3602 new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
3603 }
3604 mGoToFullShadeNeedsAnimation = false;
3605 }
3606
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003607 @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
3608 protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -05003609 return new StackScrollAlgorithm(context, this);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003610 }
3611
3612 /**
3613 * @return Whether a y coordinate is inside the content.
3614 */
3615 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
3616 public boolean isInContentBounds(float y) {
3617 return y < getHeight() - getEmptyBottomMargin();
3618 }
3619
3620 @ShadeViewRefactor(RefactorComponent.INPUT)
3621 public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
3622 mLongPressListener = listener;
3623 }
3624
3625 @Override
3626 @ShadeViewRefactor(RefactorComponent.INPUT)
3627 public boolean onTouchEvent(MotionEvent ev) {
3628 boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
3629 || ev.getActionMasked() == MotionEvent.ACTION_UP;
3630 handleEmptySpaceClick(ev);
3631 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003632 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003633 if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
3634 if (isCancelOrUp) {
3635 mExpandHelper.onlyObserveMovements(false);
3636 }
3637 boolean wasExpandingBefore = mExpandingNotification;
3638 expandWantsIt = mExpandHelper.onTouchEvent(ev);
3639 if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
3640 && !mDisallowScrollingInThisMotion) {
3641 dispatchDownEventToScroller(ev);
3642 }
3643 }
3644 boolean scrollerWantsIt = false;
3645 if (mIsExpanded && !swipingInProgress && !mExpandingNotification
3646 && !mDisallowScrollingInThisMotion) {
3647 scrollerWantsIt = onScrollTouch(ev);
3648 }
3649 boolean horizontalSwipeWantsIt = false;
3650 if (!mIsBeingDragged
3651 && !mExpandingNotification
3652 && !mExpandedInThisMotion
3653 && !mOnlyScrollingInThisMotion
3654 && !mDisallowDismissInThisMotion) {
3655 horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
3656 }
3657
3658 // Check if we need to clear any snooze leavebehinds
3659 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
3660 if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
3661 && guts.getGutsContent() instanceof NotificationSnooze) {
3662 NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
3663 if ((ns.isExpanded() && isCancelOrUp)
3664 || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
3665 // If the leavebehind is expanded we clear it on the next up event, otherwise we
3666 // clear it on the next non-horizontal swipe or expand event.
3667 checkSnoozeLeavebehind();
3668 }
3669 }
3670 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
3671 mCheckForLeavebehind = true;
3672 }
3673 return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
3674 }
3675
3676 @ShadeViewRefactor(RefactorComponent.INPUT)
3677 private void dispatchDownEventToScroller(MotionEvent ev) {
3678 MotionEvent downEvent = MotionEvent.obtain(ev);
3679 downEvent.setAction(MotionEvent.ACTION_DOWN);
3680 onScrollTouch(downEvent);
3681 downEvent.recycle();
3682 }
3683
3684 @Override
3685 @ShadeViewRefactor(RefactorComponent.INPUT)
3686 public boolean onGenericMotionEvent(MotionEvent event) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003687 if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003688 || mDisallowScrollingInThisMotion) {
3689 return false;
3690 }
3691 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
3692 switch (event.getAction()) {
3693 case MotionEvent.ACTION_SCROLL: {
3694 if (!mIsBeingDragged) {
3695 final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
3696 if (vscroll != 0) {
3697 final int delta = (int) (vscroll * getVerticalScrollFactor());
Gus Prevas0fa58d62019-01-11 13:58:40 -05003698 if (ANCHOR_SCROLLING) {
3699 mScrollAnchorViewY -= delta;
3700 updateScrollAnchor();
3701 clampScrollPosition();
3702 updateOnScrollChange();
3703 } else {
3704 final int range = getScrollRange();
3705 int oldScrollY = mOwnScrollY;
3706 int newScrollY = oldScrollY - delta;
3707 if (newScrollY < 0) {
3708 newScrollY = 0;
3709 } else if (newScrollY > range) {
3710 newScrollY = range;
3711 }
3712 if (newScrollY != oldScrollY) {
3713 setOwnScrollY(newScrollY);
3714 return true;
3715 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003716 }
3717 }
3718 }
3719 }
3720 }
3721 }
3722 return super.onGenericMotionEvent(event);
3723 }
3724
3725 @ShadeViewRefactor(RefactorComponent.INPUT)
3726 private boolean onScrollTouch(MotionEvent ev) {
3727 if (!isScrollingEnabled()) {
3728 return false;
3729 }
3730 if (isInsideQsContainer(ev) && !mIsBeingDragged) {
3731 return false;
3732 }
3733 mForcedScroll = null;
3734 initVelocityTrackerIfNotExists();
3735 mVelocityTracker.addMovement(ev);
3736
3737 final int action = ev.getAction();
3738
3739 switch (action & MotionEvent.ACTION_MASK) {
3740 case MotionEvent.ACTION_DOWN: {
3741 if (getChildCount() == 0 || !isInContentBounds(ev)) {
3742 return false;
3743 }
3744 boolean isBeingDragged = !mScroller.isFinished();
3745 setIsBeingDragged(isBeingDragged);
3746 /*
3747 * If being flinged and user touches, stop the fling. isFinished
3748 * will be false if being flinged.
3749 */
3750 if (!mScroller.isFinished()) {
3751 mScroller.forceFinished(true);
3752 }
3753
3754 // Remember where the motion event started
3755 mLastMotionY = (int) ev.getY();
3756 mDownX = (int) ev.getX();
3757 mActivePointerId = ev.getPointerId(0);
3758 break;
3759 }
3760 case MotionEvent.ACTION_MOVE:
3761 final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
3762 if (activePointerIndex == -1) {
3763 Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
3764 break;
3765 }
3766
3767 final int y = (int) ev.getY(activePointerIndex);
3768 final int x = (int) ev.getX(activePointerIndex);
3769 int deltaY = mLastMotionY - y;
3770 final int xDiff = Math.abs(x - mDownX);
3771 final int yDiff = Math.abs(deltaY);
3772 if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
3773 setIsBeingDragged(true);
3774 if (deltaY > 0) {
3775 deltaY -= mTouchSlop;
3776 } else {
3777 deltaY += mTouchSlop;
3778 }
3779 }
3780 if (mIsBeingDragged) {
3781 // Scroll to follow the motion event
3782 mLastMotionY = y;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003783 float scrollAmount;
Gus Prevas0fa58d62019-01-11 13:58:40 -05003784 int range;
3785 if (ANCHOR_SCROLLING) {
3786 range = 0; // unused in the methods it's being passed to
3787 } else {
3788 range = getScrollRange();
3789 if (mExpandedInThisMotion) {
3790 range = Math.min(range, mMaxScrollAfterExpand);
3791 }
3792 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003793 if (deltaY < 0) {
3794 scrollAmount = overScrollDown(deltaY);
3795 } else {
3796 scrollAmount = overScrollUp(deltaY, range);
3797 }
3798
3799 // Calling customOverScrollBy will call onCustomOverScrolled, which
3800 // sets the scrolling if applicable.
3801 if (scrollAmount != 0.0f) {
3802 // The scrolling motion could not be compensated with the
3803 // existing overScroll, we have to scroll the view
3804 customOverScrollBy((int) scrollAmount, mOwnScrollY,
3805 range, getHeight() / 2);
3806 // If we're scrolling, leavebehinds should be dismissed
3807 checkSnoozeLeavebehind();
3808 }
3809 }
3810 break;
3811 case MotionEvent.ACTION_UP:
3812 if (mIsBeingDragged) {
3813 final VelocityTracker velocityTracker = mVelocityTracker;
3814 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
3815 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
3816
3817 if (shouldOverScrollFling(initialVelocity)) {
3818 onOverScrollFling(true, initialVelocity);
3819 } else {
3820 if (getChildCount() > 0) {
3821 if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
3822 float currentOverScrollTop = getCurrentOverScrollAmount(true);
3823 if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
3824 fling(-initialVelocity);
3825 } else {
3826 onOverScrollFling(false, initialVelocity);
3827 }
3828 } else {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003829 if (ANCHOR_SCROLLING) {
3830 // TODO
3831 } else {
3832 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3833 getScrollRange())) {
3834 animateScroll();
3835 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003836 }
3837 }
3838 }
3839 }
3840 mActivePointerId = INVALID_POINTER;
3841 endDrag();
3842 }
3843
3844 break;
3845 case MotionEvent.ACTION_CANCEL:
3846 if (mIsBeingDragged && getChildCount() > 0) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05003847 if (ANCHOR_SCROLLING) {
3848 // TODO
3849 } else {
3850 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
3851 getScrollRange())) {
3852 animateScroll();
3853 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003854 }
3855 mActivePointerId = INVALID_POINTER;
3856 endDrag();
3857 }
3858 break;
3859 case MotionEvent.ACTION_POINTER_DOWN: {
3860 final int index = ev.getActionIndex();
3861 mLastMotionY = (int) ev.getY(index);
3862 mDownX = (int) ev.getX(index);
3863 mActivePointerId = ev.getPointerId(index);
3864 break;
3865 }
3866 case MotionEvent.ACTION_POINTER_UP:
3867 onSecondaryPointerUp(ev);
3868 mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
3869 mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
3870 break;
3871 }
3872 return true;
3873 }
3874
3875 @ShadeViewRefactor(RefactorComponent.INPUT)
3876 protected boolean isInsideQsContainer(MotionEvent ev) {
3877 return ev.getY() < mQsContainer.getBottom();
3878 }
3879
3880 @ShadeViewRefactor(RefactorComponent.INPUT)
3881 private void onOverScrollFling(boolean open, int initialVelocity) {
3882 if (mOverscrollTopChangedListener != null) {
3883 mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
3884 }
3885 mDontReportNextOverScroll = true;
3886 setOverScrollAmount(0.0f, true, false);
3887 }
3888
3889
3890 @ShadeViewRefactor(RefactorComponent.INPUT)
3891 private void onSecondaryPointerUp(MotionEvent ev) {
3892 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
3893 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
3894 final int pointerId = ev.getPointerId(pointerIndex);
3895 if (pointerId == mActivePointerId) {
3896 // This was our active pointer going up. Choose a new
3897 // active pointer and adjust accordingly.
3898 // TODO: Make this decision more intelligent.
3899 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
3900 mLastMotionY = (int) ev.getY(newPointerIndex);
3901 mActivePointerId = ev.getPointerId(newPointerIndex);
3902 if (mVelocityTracker != null) {
3903 mVelocityTracker.clear();
3904 }
3905 }
3906 }
3907
3908 @ShadeViewRefactor(RefactorComponent.INPUT)
3909 private void endDrag() {
3910 setIsBeingDragged(false);
3911
3912 recycleVelocityTracker();
3913
3914 if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
3915 setOverScrollAmount(0, true /* onTop */, true /* animate */);
3916 }
3917 if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
3918 setOverScrollAmount(0, false /* onTop */, true /* animate */);
3919 }
3920 }
3921
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003922 @Override
3923 @ShadeViewRefactor(RefactorComponent.INPUT)
3924 public boolean onInterceptTouchEvent(MotionEvent ev) {
3925 initDownStates(ev);
3926 handleEmptySpaceClick(ev);
3927 boolean expandWantsIt = false;
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05003928 boolean swipingInProgress = mSwipingInProgress;
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04003929 if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
3930 expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
3931 }
3932 boolean scrollWantsIt = false;
3933 if (!swipingInProgress && !mExpandingNotification) {
3934 scrollWantsIt = onInterceptTouchEventScroll(ev);
3935 }
3936 boolean swipeWantsIt = false;
3937 if (!mIsBeingDragged
3938 && !mExpandingNotification
3939 && !mExpandedInThisMotion
3940 && !mOnlyScrollingInThisMotion
3941 && !mDisallowDismissInThisMotion) {
3942 swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
3943 }
3944 // Check if we need to clear any snooze leavebehinds
3945 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
3946 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
3947 if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
3948 !expandWantsIt && !scrollWantsIt) {
3949 mCheckForLeavebehind = false;
3950 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
3951 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
3952 false /* resetMenu */);
3953 }
3954 if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
3955 mCheckForLeavebehind = true;
3956 }
3957 return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
3958 }
3959
3960 @ShadeViewRefactor(RefactorComponent.INPUT)
3961 private void handleEmptySpaceClick(MotionEvent ev) {
3962 switch (ev.getActionMasked()) {
3963 case MotionEvent.ACTION_MOVE:
3964 if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
3965 || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
3966 mTouchIsClick = false;
3967 }
3968 break;
3969 case MotionEvent.ACTION_UP:
3970 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
3971 isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
3972 mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
3973 }
3974 break;
3975 }
3976 }
3977
3978 @ShadeViewRefactor(RefactorComponent.INPUT)
3979 private void initDownStates(MotionEvent ev) {
3980 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
3981 mExpandedInThisMotion = false;
3982 mOnlyScrollingInThisMotion = !mScroller.isFinished();
3983 mDisallowScrollingInThisMotion = false;
3984 mDisallowDismissInThisMotion = false;
3985 mTouchIsClick = true;
3986 mInitialTouchX = ev.getX();
3987 mInitialTouchY = ev.getY();
3988 }
3989 }
3990
3991 @Override
3992 @ShadeViewRefactor(RefactorComponent.INPUT)
3993 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3994 super.requestDisallowInterceptTouchEvent(disallowIntercept);
3995 if (disallowIntercept) {
3996 cancelLongPress();
3997 }
3998 }
3999
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004000 @ShadeViewRefactor(RefactorComponent.INPUT)
Selim Cinek67b22602014-03-10 15:40:16 +01004001 private boolean onInterceptTouchEventScroll(MotionEvent ev) {
Selim Cinek1408eb52014-06-02 14:45:38 +02004002 if (!isScrollingEnabled()) {
4003 return false;
4004 }
Selim Cinek67b22602014-03-10 15:40:16 +01004005 /*
4006 * This method JUST determines whether we want to intercept the motion.
4007 * If we return true, onMotionEvent will be called and we do the actual
4008 * scrolling there.
4009 */
4010
4011 /*
Jason Monke59dc402018-08-16 12:05:01 -04004012 * Shortcut the most recurring case: the user is in the dragging
4013 * state and is moving their finger. We want to intercept this
4014 * motion.
4015 */
Selim Cinek67b22602014-03-10 15:40:16 +01004016 final int action = ev.getAction();
4017 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
4018 return true;
4019 }
4020
Selim Cinek67b22602014-03-10 15:40:16 +01004021 switch (action & MotionEvent.ACTION_MASK) {
4022 case MotionEvent.ACTION_MOVE: {
4023 /*
4024 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
Chris Wren5d53df42015-06-26 11:26:03 -04004025 * whether the user has moved far enough from the original down touch.
Selim Cinek67b22602014-03-10 15:40:16 +01004026 */
4027
4028 /*
Jason Monke59dc402018-08-16 12:05:01 -04004029 * Locally do absolute value. mLastMotionY is set to the y value
4030 * of the down event.
4031 */
Selim Cinek67b22602014-03-10 15:40:16 +01004032 final int activePointerId = mActivePointerId;
4033 if (activePointerId == INVALID_POINTER) {
4034 // If we don't have a valid id, the touch down wasn't on content.
4035 break;
4036 }
4037
4038 final int pointerIndex = ev.findPointerIndex(activePointerId);
4039 if (pointerIndex == -1) {
4040 Log.e(TAG, "Invalid pointerId=" + activePointerId
4041 + " in onInterceptTouchEvent");
4042 break;
4043 }
4044
4045 final int y = (int) ev.getY(pointerIndex);
Selim Cinek1408eb52014-06-02 14:45:38 +02004046 final int x = (int) ev.getX(pointerIndex);
Selim Cinek67b22602014-03-10 15:40:16 +01004047 final int yDiff = Math.abs(y - mLastMotionY);
Selim Cinek1408eb52014-06-02 14:45:38 +02004048 final int xDiff = Math.abs(x - mDownX);
4049 if (yDiff > mTouchSlop && yDiff > xDiff) {
Selim Cinek67b22602014-03-10 15:40:16 +01004050 setIsBeingDragged(true);
4051 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004052 mDownX = x;
Selim Cinek67b22602014-03-10 15:40:16 +01004053 initVelocityTrackerIfNotExists();
4054 mVelocityTracker.addMovement(ev);
Selim Cinek67b22602014-03-10 15:40:16 +01004055 }
4056 break;
4057 }
4058
4059 case MotionEvent.ACTION_DOWN: {
4060 final int y = (int) ev.getY();
Jayasri bhattacharyya5e55c892015-09-10 16:00:10 +05304061 mScrolledToTopOnFirstDown = isScrolledToTop();
Selim Cinek34ed7c02017-09-08 15:03:12 -07004062 if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
Selim Cinek67b22602014-03-10 15:40:16 +01004063 setIsBeingDragged(false);
4064 recycleVelocityTracker();
4065 break;
4066 }
4067
4068 /*
4069 * Remember location of down touch.
4070 * ACTION_DOWN always refers to pointer index 0.
4071 */
4072 mLastMotionY = y;
Selim Cinek1408eb52014-06-02 14:45:38 +02004073 mDownX = (int) ev.getX();
Selim Cinek67b22602014-03-10 15:40:16 +01004074 mActivePointerId = ev.getPointerId(0);
4075
4076 initOrResetVelocityTracker();
4077 mVelocityTracker.addMovement(ev);
4078 /*
Jason Monke59dc402018-08-16 12:05:01 -04004079 * If being flinged and user touches the screen, initiate drag;
4080 * otherwise don't. mScroller.isFinished should be false when
4081 * being flinged.
4082 */
Selim Cinek67b22602014-03-10 15:40:16 +01004083 boolean isBeingDragged = !mScroller.isFinished();
4084 setIsBeingDragged(isBeingDragged);
4085 break;
4086 }
4087
4088 case MotionEvent.ACTION_CANCEL:
4089 case MotionEvent.ACTION_UP:
4090 /* Release the drag */
4091 setIsBeingDragged(false);
4092 mActivePointerId = INVALID_POINTER;
4093 recycleVelocityTracker();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004094 if (ANCHOR_SCROLLING) {
4095 // TODO
4096 } else {
4097 if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
4098 animateScroll();
4099 }
Selim Cinek67b22602014-03-10 15:40:16 +01004100 }
4101 break;
4102 case MotionEvent.ACTION_POINTER_UP:
4103 onSecondaryPointerUp(ev);
4104 break;
4105 }
4106
4107 /*
Jason Monke59dc402018-08-16 12:05:01 -04004108 * The only time we want to intercept motion events is if we are in the
4109 * drag mode.
4110 */
Selim Cinek67b22602014-03-10 15:40:16 +01004111 return mIsBeingDragged;
4112 }
4113
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004114 /**
4115 * @return Whether the specified motion event is actually happening over the content.
4116 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004117 @ShadeViewRefactor(RefactorComponent.INPUT)
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004118 private boolean isInContentBounds(MotionEvent event) {
Selim Cinekab1dc952014-10-30 20:20:29 +01004119 return isInContentBounds(event.getY());
4120 }
4121
Jorim Jaggife6bfa62014-05-07 23:23:18 +02004122
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004123 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004124 @ShadeViewRefactor(RefactorComponent.INPUT)
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004125 void setIsBeingDragged(boolean isDragged) {
Selim Cinek67b22602014-03-10 15:40:16 +01004126 mIsBeingDragged = isDragged;
4127 if (isDragged) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004128 requestDisallowInterceptTouchEvent(true);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004129 cancelLongPress();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004130 resetExposedMenuView(true /* animate */, true /* force */);
Selim Cinek67b22602014-03-10 15:40:16 +01004131 }
4132 }
4133
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004134 @ShadeViewRefactor(RefactorComponent.INPUT)
4135 public void requestDisallowLongPress() {
4136 cancelLongPress();
4137 }
4138
4139 @ShadeViewRefactor(RefactorComponent.INPUT)
4140 public void requestDisallowDismiss() {
4141 mDisallowDismissInThisMotion = true;
4142 }
4143
4144 @ShadeViewRefactor(RefactorComponent.INPUT)
4145 public void cancelLongPress() {
4146 mSwipeHelper.cancelLongPress();
4147 }
4148
4149 @ShadeViewRefactor(RefactorComponent.INPUT)
4150 public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
4151 mOnEmptySpaceClickListener = listener;
4152 }
4153
4154 /** @hide */
4155 @Override
4156 @ShadeViewRefactor(RefactorComponent.INPUT)
4157 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
4158 if (super.performAccessibilityActionInternal(action, arguments)) {
4159 return true;
4160 }
4161 if (!isEnabled()) {
4162 return false;
4163 }
4164 int direction = -1;
4165 switch (action) {
4166 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
4167 // fall through
4168 case android.R.id.accessibilityActionScrollDown:
4169 direction = 1;
4170 // fall through
4171 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
4172 // fall through
4173 case android.R.id.accessibilityActionScrollUp:
Gus Prevas0fa58d62019-01-11 13:58:40 -05004174 if (ANCHOR_SCROLLING) {
4175 // TODO
4176 } else {
4177 final int viewportHeight =
4178 getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
4179 - mShelf.getIntrinsicHeight();
4180 final int targetScrollY = Math.max(0,
4181 Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
4182 if (targetScrollY != mOwnScrollY) {
4183 mScroller.startScroll(mScrollX, mOwnScrollY, 0,
4184 targetScrollY - mOwnScrollY);
4185 animateScroll();
4186 return true;
4187 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04004188 }
4189 break;
4190 }
4191 return false;
4192 }
4193
4194 @ShadeViewRefactor(RefactorComponent.INPUT)
4195 public void closeControlsIfOutsideTouch(MotionEvent ev) {
4196 NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
4197 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
4198 View translatingParentView = mSwipeHelper.getTranslatingParentView();
4199 View view = null;
4200 if (guts != null && !guts.getGutsContent().isLeavebehind()) {
4201 // Only close visible guts if they're not a leavebehind.
4202 view = guts;
4203 } else if (menuRow != null && menuRow.isMenuVisible()
4204 && translatingParentView != null) {
4205 // Checking menu
4206 view = translatingParentView;
4207 }
4208 if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
4209 // Touch was outside visible guts / menu notification, close what's visible
4210 mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
4211 false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
4212 false /* resetMenu */);
4213 resetExposedMenuView(true /* animate */, true /* force */);
4214 }
4215 }
4216
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05004217 @ShadeViewRefactor(RefactorComponent.INPUT)
4218 private void setSwipingInProgress(boolean swiping) {
4219 mSwipingInProgress = swiping;
4220 if (swiping) {
4221 requestDisallowInterceptTouchEvent(true);
4222 }
4223 }
4224
Selim Cinek67b22602014-03-10 15:40:16 +01004225 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004226 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek67b22602014-03-10 15:40:16 +01004227 public void onWindowFocusChanged(boolean hasWindowFocus) {
4228 super.onWindowFocusChanged(hasWindowFocus);
4229 if (!hasWindowFocus) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00004230 cancelLongPress();
Selim Cinek67b22602014-03-10 15:40:16 +01004231 }
4232 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004233
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07004234 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004235 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Adrian Roos181385c2016-05-05 17:45:44 -04004236 public void clearChildFocus(View child) {
4237 super.clearChildFocus(child);
4238 if (mForcedScroll == child) {
4239 mForcedScroll = null;
4240 }
4241 }
4242
Selim Cinekfab078b2014-03-27 22:45:58 +01004243 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004244 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfab078b2014-03-27 22:45:58 +01004245 public boolean isScrolledToTop() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004246 if (ANCHOR_SCROLLING) {
4247 updateScrollAnchor();
4248 // TODO: once we're recycling this will need to check the adapter position of the child
4249 return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0;
4250 } else {
4251 return mOwnScrollY == 0;
4252 }
Selim Cinekfab078b2014-03-27 22:45:58 +01004253 }
4254
4255 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004256 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004257 public boolean isScrolledToBottom() {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004258 if (ANCHOR_SCROLLING) {
Gus Prevascdc98342019-01-14 14:29:44 -05004259 return getMaxPositiveScrollAmount() <= 0;
Gus Prevas0fa58d62019-01-11 13:58:40 -05004260 } else {
4261 return mOwnScrollY >= getScrollRange();
4262 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004263 }
4264
4265 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004266 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfab078b2014-03-27 22:45:58 +01004267 public View getHostView() {
4268 return this;
4269 }
Christoph Studer6e3eceb2014-04-01 18:40:27 +02004270
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004271 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004272 public int getEmptyBottomMargin() {
Selim Cinekdb167372016-11-17 15:41:17 -08004273 return Math.max(mMaxLayoutHeight - mContentHeight, 0);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01004274 }
4275
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004276 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004277 public void checkSnoozeLeavebehind() {
4278 if (mCheckForLeavebehind) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04004279 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
Eliot Courtney47098cb2017-10-18 17:30:30 +09004280 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
4281 false /* resetMenu */);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004282 mCheckForLeavebehind = false;
4283 }
4284 }
4285
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004286 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellorc2dbe492017-03-30 13:22:03 -07004287 public void resetCheckSnoozeLeavebehind() {
4288 mCheckForLeavebehind = true;
4289 }
4290
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004291 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004292 public void onExpansionStarted() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004293 mIsExpansionChanging = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004294 mAmbientState.setExpansionChanging(true);
Mady Mellorc2dbe492017-03-30 13:22:03 -07004295 checkSnoozeLeavebehind();
Selim Cinek1685e632014-04-08 02:27:49 +02004296 }
4297
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004298 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek1685e632014-04-08 02:27:49 +02004299 public void onExpansionStopped() {
Selim Cinekc27437b2014-05-14 10:23:33 +02004300 mIsExpansionChanging = false;
Mady Mellorc2dbe492017-03-30 13:22:03 -07004301 resetCheckSnoozeLeavebehind();
Selim Cinekd5ab6452016-12-08 16:34:00 -08004302 mAmbientState.setExpansionChanging(false);
Selim Cinek4fe3e472014-07-03 16:32:54 +02004303 if (!mIsExpanded) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05004304 resetScrollPosition();
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004305 mStatusBar.resetUserExpandedStates();
Selim Cinek5b1591a2017-07-03 17:05:01 +02004306 clearTemporaryViews();
4307 clearUserLockedViews();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05004308 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08004309 if (draggedViews.size() > 0) {
4310 draggedViews.clear();
4311 updateContinuousShadowDrawing();
4312 }
Selim Cinek5b1591a2017-07-03 17:05:01 +02004313 }
4314 }
Selim Cinekf336f4c2014-11-12 16:58:16 +01004315
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004316 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004317 private void clearUserLockedViews() {
4318 for (int i = 0; i < getChildCount(); i++) {
4319 ExpandableView child = (ExpandableView) getChildAt(i);
4320 if (child instanceof ExpandableNotificationRow) {
4321 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
4322 row.setUserLocked(false);
4323 }
4324 }
4325 }
4326
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004327 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek5b1591a2017-07-03 17:05:01 +02004328 private void clearTemporaryViews() {
Selim Cinek9dd0d042018-05-14 18:12:42 -07004329 // lets make sure nothing is transient anymore
Rohan Shah8ee53652018-04-05 11:13:50 -07004330 clearTemporaryViewsInGroup(this);
Selim Cinek5b1591a2017-07-03 17:05:01 +02004331 for (int i = 0; i < getChildCount(); i++) {
4332 ExpandableView child = (ExpandableView) getChildAt(i);
4333 if (child instanceof ExpandableNotificationRow) {
4334 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Rohan Shah8ee53652018-04-05 11:13:50 -07004335 clearTemporaryViewsInGroup(row.getChildrenContainer());
Selim Cinekd1395642016-04-28 12:22:42 -07004336 }
Selim Cinek4fe3e472014-07-03 16:32:54 +02004337 }
Selim Cinek1685e632014-04-08 02:27:49 +02004338 }
4339
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004340 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Rohan Shah8ee53652018-04-05 11:13:50 -07004341 private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
Selim Cinekd1395642016-04-28 12:22:42 -07004342 while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
Selim Cinek81f26d32016-05-09 18:54:10 -04004343 viewGroup.removeTransientView(viewGroup.getTransientView(0));
Selim Cinekd1395642016-04-28 12:22:42 -07004344 }
4345 }
4346
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004347 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004348 public void onPanelTrackingStarted() {
4349 mPanelTracking = true;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004350 mAmbientState.setPanelTracking(true);
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004351 resetExposedMenuView(true /* animate */, true /* force */);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004352 }
Jason Monke59dc402018-08-16 12:05:01 -04004353
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004354 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004355 public void onPanelTrackingStopped() {
4356 mPanelTracking = false;
Selim Cinekd5ab6452016-12-08 16:34:00 -08004357 mAmbientState.setPanelTracking(false);
Jorim Jaggie4b840d2015-06-30 16:19:17 -07004358 }
4359
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004360 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb24e0a92015-06-09 20:17:30 -07004361 public void resetScrollPosition() {
4362 mScroller.abortAnimation();
Gus Prevas0fa58d62019-01-11 13:58:40 -05004363 if (ANCHOR_SCROLLING) {
4364 // TODO: once we're recycling this will need to modify the adapter position instead
4365 mScrollAnchorView = getFirstChildNotGone();
4366 mScrollAnchorViewY = 0;
4367 updateOnScrollChange();
4368 } else {
4369 setOwnScrollY(0);
4370 }
Selim Cinekb24e0a92015-06-09 20:17:30 -07004371 }
4372
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004373 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek3d6ae232019-01-04 14:14:33 -08004374 private void setIsExpanded(boolean isExpanded) {
Selim Cinekcab4a602014-09-03 14:47:57 +02004375 boolean changed = isExpanded != mIsExpanded;
Selim Cinek572bbd42014-04-25 16:43:27 +02004376 mIsExpanded = isExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +02004377 mStackScrollAlgorithm.setIsExpanded(isExpanded);
Selim Cinek3d6ae232019-01-04 14:14:33 -08004378 mAmbientState.setShadeExpanded(isExpanded);
4379 mStateAnimator.setShadeExpanded(isExpanded);
shawnlin3a950c32019-05-15 20:06:10 +08004380 mSwipeHelper.setIsExpanded(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 /**
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004715 * See {@link AmbientState#setDozing}.
John Spurlockbf370992014-06-17 13:58:31 -04004716 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004717 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004718 public void setDozing(boolean dozing, boolean animate,
4719 @Nullable PointF touchWakeUpScreenLocation) {
4720 if (mAmbientState.isDozing() == dozing) {
Adrian Roos260c1f72017-08-07 15:52:26 +02004721 return;
4722 }
Selim Cinekc1d9ab22019-05-21 18:08:30 -07004723 mAmbientState.setDozing(dozing);
John Spurlockbf370992014-06-17 13:58:31 -04004724 if (animate && mAnimationsEnabled) {
4725 mDarkNeedsAnimation = true;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004726 mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004727 mNeedsAnimation = true;
Selim Cinek6811d722016-01-19 17:53:12 -08004728 }
Lucas Dupin8e9fa2d2018-01-29 15:36:35 -08004729 requestChildrenUpdate();
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004730 updateWillNotDraw();
Adrian Roos7a9551a2017-01-11 12:27:49 -08004731 notifyHeightChangeListener(mShelf);
John Spurlockbf370992014-06-17 13:58:31 -04004732 }
4733
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004734 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Bill Line60aa1e2018-06-13 18:07:15 +08004735 private void updatePanelTranslation() {
Lucas Dupinb46d0a22019-01-11 16:57:16 -08004736 setTranslationX(mHorizontalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
Bill Line60aa1e2018-06-13 18:07:15 +08004737 }
4738
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004739 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupinb46d0a22019-01-11 16:57:16 -08004740 public void setHorizontalPanelTranslation(float verticalPanelTranslation) {
4741 mHorizontalPanelTranslation = verticalPanelTranslation;
Bill Line60aa1e2018-06-13 18:07:15 +08004742 updatePanelTranslation();
Lucas Dupin0cd882f2018-01-30 12:19:49 -08004743 }
4744
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004745 /**
4746 * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
4747 * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
4748 * {@link #mAmbientState}'s dark mode is toggled.
4749 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004750 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004751 private void updateWillNotDraw() {
Lucas Dupind285cf02018-01-18 09:18:23 -08004752 boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
Adrian Roosf0b4f962017-05-25 11:53:11 -07004753 setWillNotDraw(!willDraw);
Anthony Chen3cb3ad92016-12-01 10:58:47 -08004754 }
4755
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004756 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupind285cf02018-01-18 09:18:23 -08004757 private void setDarkAmount(float darkAmount) {
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004758 setDarkAmount(darkAmount, darkAmount);
4759 }
4760
4761 /**
4762 * Sets the current dark amount.
4763 *
Jason Monke59dc402018-08-16 12:05:01 -04004764 * @param linearDarkAmount The dark amount that follows linear interpoloation in the
4765 * animation,
4766 * i.e. animates from 0 to 1 or vice-versa in a linear manner.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004767 * @param interpolatedDarkAmount The dark amount that follows the actual interpolation of the
Jason Monke59dc402018-08-16 12:05:01 -04004768 * animation curve.
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004769 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004770 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004771 public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
4772 mLinearDarkAmount = linearDarkAmount;
4773 mInterpolatedDarkAmount = interpolatedDarkAmount;
Lucas Dupinb561eda2018-04-09 17:25:04 -07004774 boolean wasFullyDark = mAmbientState.isFullyDark();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004775 boolean wasDarkAtAll = mAmbientState.isDarkAtAll();
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004776 mAmbientState.setDarkAmount(interpolatedDarkAmount);
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004777 boolean nowFullyDark = mAmbientState.isFullyDark();
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004778 boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
Selim Cinek9bfc7a52018-06-11 16:09:00 -07004779 if (nowFullyDark != wasFullyDark) {
Lucas Dupin16cfe452018-02-08 13:14:50 -08004780 updateContentHeight();
Selim Cinek1e059272019-05-21 18:15:00 -07004781 if (nowFullyDark) {
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004782 updateDarkShelfVisibility();
4783 }
Lucas Dupin16cfe452018-02-08 13:14:50 -08004784 }
Gus Prevas99ba4ba2018-10-01 16:40:23 -04004785 if (!wasDarkAtAll && nowDarkAtAll) {
4786 resetExposedMenuView(true /* animate */, true /* animate */);
4787 }
Lucas Dupin64e2f572019-03-21 14:21:14 -07004788 if (nowFullyDark != wasFullyDark || wasDarkAtAll != nowDarkAtAll) {
4789 invalidateOutline();
4790 }
Lucas Dupin60661a62018-04-12 10:50:13 -07004791 updateAlgorithmHeightAndPadding();
Selim Cinek972123d2016-05-03 14:25:58 -07004792 updateBackgroundDimming();
Bill Line60aa1e2018-06-13 18:07:15 +08004793 updatePanelTranslation();
Lucas Dupinb561eda2018-04-09 17:25:04 -07004794 requestChildrenUpdate();
Selim Cinek972123d2016-05-03 14:25:58 -07004795 }
4796
Lucas Dupin7fc9dc12019-01-03 09:19:43 -08004797 private void updateDarkShelfVisibility() {
4798 DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
4799 if (dozeParameters.shouldControlScreenOff()) {
4800 mShelf.fadeInTranslating();
4801 }
4802 updateClipping();
4803 }
4804
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004805 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggic4cf07a2018-07-05 18:28:12 +02004806 public void notifyDarkAnimationStart(boolean dark) {
4807 // We only swap the scaling factor if we're fully dark or fully awake to avoid
4808 // interpolation issues when playing with the power button.
4809 if (mInterpolatedDarkAmount == 0 || mInterpolatedDarkAmount == 1) {
4810 mBackgroundXFactor = dark ? 1.8f : 1.5f;
4811 mDarkXInterpolator = dark
4812 ? Interpolators.FAST_OUT_SLOW_IN_REVERSE
4813 : Interpolators.FAST_OUT_SLOW_IN;
4814 }
Selim Cinek972123d2016-05-03 14:25:58 -07004815 }
4816
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004817 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004818 private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
Selim Cinekbc243a92016-09-27 16:35:13 -07004819 if (screenLocation == null || screenLocation.y < mTopPadding) {
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004820 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4821 }
4822 if (screenLocation.y > getBottomMostNotificationBottom()) {
4823 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
4824 }
4825 View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
4826 if (child != null) {
4827 return getNotGoneIndex(child);
4828 } else {
4829 return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
4830 }
4831 }
4832
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004833 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01004834 private int getNotGoneIndex(View child) {
4835 int count = getChildCount();
4836 int notGoneIndex = 0;
4837 for (int i = 0; i < count; i++) {
4838 View v = getChildAt(i);
4839 if (child == v) {
4840 return notGoneIndex;
4841 }
4842 if (v.getVisibility() != View.GONE) {
4843 notGoneIndex++;
4844 }
4845 }
4846 return -1;
4847 }
4848
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004849 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004850 public void setFooterView(@NonNull FooterView footerView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004851 int index = -1;
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004852 if (mFooterView != null) {
4853 index = indexOfChild(mFooterView);
4854 removeView(mFooterView);
Selim Cinek01af3342016-02-09 19:25:31 -08004855 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004856 mFooterView = footerView;
4857 addView(mFooterView, index);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004858 }
4859
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004860 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004861 public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
Selim Cinek01af3342016-02-09 19:25:31 -08004862 int index = -1;
4863 if (mEmptyShadeView != null) {
4864 index = indexOfChild(mEmptyShadeView);
4865 removeView(mEmptyShadeView);
4866 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004867 mEmptyShadeView = emptyShadeView;
Selim Cinek01af3342016-02-09 19:25:31 -08004868 addView(mEmptyShadeView, index);
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004869 }
4870
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004871 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004872 public void updateEmptyShadeView(boolean visible) {
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004873 mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
Lucas Dupinc9274ff2018-05-09 17:40:20 -07004874
4875 int oldTextRes = mEmptyShadeView.getTextResource();
4876 int newTextRes = mStatusBar.areNotificationsHidden()
4877 ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
4878 if (oldTextRes != newTextRes) {
4879 mEmptyShadeView.setText(newTextRes);
4880 }
Jorim Jaggia2052ea2014-08-05 16:22:30 +02004881 }
4882
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004883 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004884 public void updateFooterView(boolean visible, boolean showDismissView) {
4885 if (mFooterView == null) {
Anthony Chen5e3742e2017-04-07 14:28:44 -07004886 return;
4887 }
Selim Cinekd60ef9e2018-05-16 16:01:05 -07004888 boolean animate = mIsExpanded && mAnimationsEnabled;
4889 mFooterView.setVisible(visible, animate);
4890 mFooterView.setSecondaryVisible(showDismissView, animate);
Dan Sandlereceda3d2014-07-21 15:35:01 -04004891 }
4892
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004893 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Dan Sandlereceda3d2014-07-21 15:35:01 -04004894 public void setDismissAllInProgress(boolean dismissAllInProgress) {
4895 mDismissAllInProgress = dismissAllInProgress;
Selim Cinek9c17b772015-07-07 20:37:09 -07004896 mAmbientState.setDismissAllInProgress(dismissAllInProgress);
Selim Cinek9c17b772015-07-07 20:37:09 -07004897 handleDismissAllClipping();
4898 }
4899
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004900 @ShadeViewRefactor(RefactorComponent.ADAPTER)
Selim Cinek9c17b772015-07-07 20:37:09 -07004901 private void handleDismissAllClipping() {
4902 final int count = getChildCount();
4903 boolean previousChildWillBeDismissed = false;
4904 for (int i = 0; i < count; i++) {
4905 ExpandableView child = (ExpandableView) getChildAt(i);
4906 if (child.getVisibility() == GONE) {
4907 continue;
4908 }
4909 if (mDismissAllInProgress && previousChildWillBeDismissed) {
4910 child.setMinClipTopAmount(child.getClipTopAmount());
4911 } else {
4912 child.setMinClipTopAmount(0);
4913 }
Ned Burns61269442019-05-02 18:27:23 -04004914 previousChildWillBeDismissed = StackScrollAlgorithm.canChildBeDismissed(child);
Selim Cinek9c17b772015-07-07 20:37:09 -07004915 }
Selim Cineka272dfe2015-02-20 18:12:28 +01004916 }
4917
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004918 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004919 public boolean isFooterViewNotGone() {
4920 return mFooterView != null
4921 && mFooterView.getVisibility() != View.GONE
4922 && !mFooterView.willBeGone();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004923 }
4924
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004925 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek38475442018-05-18 11:11:46 -07004926 public boolean isFooterViewContentVisible() {
4927 return mFooterView != null && mFooterView.isContentVisible();
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004928 }
4929
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004930 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004931 public int getFooterViewHeight() {
4932 return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02004933 }
4934
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004935 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi0cce70c2014-11-04 16:13:41 +01004936 public int getEmptyShadeViewHeight() {
4937 return mEmptyShadeView.getHeight();
4938 }
4939
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004940 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004941 public float getBottomMostNotificationBottom() {
4942 final int count = getChildCount();
4943 float max = 0;
4944 for (int childIdx = 0; childIdx < count; childIdx++) {
4945 ExpandableView child = (ExpandableView) getChildAt(childIdx);
4946 if (child.getVisibility() == GONE) {
4947 continue;
4948 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004949 float bottom = child.getTranslationY() + child.getActualHeight()
4950 - child.getClipBottomAmount();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004951 if (bottom > max) {
4952 max = bottom;
4953 }
4954 }
Selim Cinekd2281152015-04-10 14:37:46 -07004955 return max + getStackTranslation();
Jorim Jaggie0640dd2014-08-05 23:12:40 +02004956 }
4957
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004958 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk2a6ea9c2017-01-26 11:14:51 -05004959 public void setStatusBar(StatusBar statusBar) {
4960 this.mStatusBar = statusBar;
Selim Cinek19c8c702014-08-25 22:09:19 +02004961 }
4962
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004963 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekb5605e52015-02-20 18:21:41 +01004964 public void setGroupManager(NotificationGroupManager groupManager) {
4965 this.mGroupManager = groupManager;
Kevin01a53cb2018-11-09 18:19:54 -08004966 mGroupManager.addOnGroupChangeListener(mOnGroupChangeListener);
Selim Cinek379ff8f2015-02-20 17:03:16 +01004967 }
4968
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004969 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek379ff8f2015-02-20 17:03:16 +01004970 private void requestAnimateEverything() {
Selim Cinekd9acca52014-09-01 22:33:25 +02004971 if (mIsExpanded && mAnimationsEnabled) {
4972 mEverythingNeedsAnimation = true;
Selim Cinek379ff8f2015-02-20 17:03:16 +01004973 mNeedsAnimation = true;
Selim Cinekd9acca52014-09-01 22:33:25 +02004974 requestChildrenUpdate();
4975 }
4976 }
4977
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04004978 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek04fb2582015-06-02 19:58:09 +02004979 public boolean isBelowLastNotification(float touchX, float touchY) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004980 int childCount = getChildCount();
4981 for (int i = childCount - 1; i >= 0; i--) {
4982 ExpandableView child = (ExpandableView) getChildAt(i);
4983 if (child.getVisibility() != View.GONE) {
4984 float childTop = child.getY();
4985 if (childTop > touchY) {
4986 // we are above a notification entirely let's abort
4987 return false;
4988 }
Selim Cineka686b2c2016-10-26 13:58:27 -07004989 boolean belowChild = touchY > childTop + child.getActualHeight()
4990 - child.getClipBottomAmount();
Julia Reynoldsed1c9af2018-03-21 15:21:09 -04004991 if (child == mFooterView) {
Jason Monke59dc402018-08-16 12:05:01 -04004992 if (!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
4993 touchY - childTop)) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01004994 // We clicked on the dismiss button
4995 return false;
4996 }
4997 } else if (child == mEmptyShadeView) {
4998 // We arrived at the empty shade view, for which we accept all clicks
4999 return true;
Jason Monke59dc402018-08-16 12:05:01 -04005000 } else if (!belowChild) {
Selim Cinekabf60bb2015-02-20 17:36:10 +01005001 // We are on a child
5002 return false;
5003 }
5004 }
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005005 }
Selim Cinek04fb2582015-06-02 19:58:09 +02005006 return touchY > mTopPadding + mStackTranslation;
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005007 }
5008
Selim Cinekc22fff62016-05-20 12:44:30 -07005009 /** @hide */
5010 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005011 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07005012 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
5013 super.onInitializeAccessibilityEventInternal(event);
5014 event.setScrollable(mScrollable);
5015 event.setScrollX(mScrollX);
Selim Cinekc22fff62016-05-20 12:44:30 -07005016 event.setMaxScrollX(mScrollX);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005017 if (ANCHOR_SCROLLING) {
5018 // TODO
5019 } else {
5020 event.setScrollY(mOwnScrollY);
5021 event.setMaxScrollY(getScrollRange());
5022 }
Selim Cinekc22fff62016-05-20 12:44:30 -07005023 }
5024
5025 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005026 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekc22fff62016-05-20 12:44:30 -07005027 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
5028 super.onInitializeAccessibilityNodeInfoInternal(info);
Selim Cinekef406062016-09-29 17:33:13 -07005029 if (mScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005030 info.setScrollable(true);
Selim Cinekef406062016-09-29 17:33:13 -07005031 if (mBackwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005032 info.addAction(
5033 AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
5034 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
5035 }
Selim Cinekef406062016-09-29 17:33:13 -07005036 if (mForwardScrollable) {
Selim Cinekc22fff62016-05-20 12:44:30 -07005037 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
5038 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
5039 }
5040 }
Selim Cinek41fe89a2016-06-02 15:27:56 -07005041 // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
5042 info.setClassName(ScrollView.class.getName());
Selim Cinekc22fff62016-05-20 12:44:30 -07005043 }
5044
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005045 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekb5605e52015-02-20 18:21:41 +01005046 public void generateChildOrderChangedEvent() {
5047 if (mIsExpanded && mAnimationsEnabled) {
5048 mGenerateChildOrderChangedEvent = true;
5049 mNeedsAnimation = true;
5050 requestChildrenUpdate();
5051 }
5052 }
5053
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005054 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005055 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005056 public int getContainerChildCount() {
5057 return getChildCount();
5058 }
5059
5060 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005061 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005062 public View getContainerChildAt(int i) {
5063 return getChildAt(i);
5064 }
5065
5066 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005067 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005068 public void removeContainerView(View v) {
5069 removeView(v);
5070 }
5071
5072 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005073 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005074 public void addContainerView(View v) {
5075 addView(v);
5076 }
5077
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005078 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek684a4422015-04-15 16:18:39 -07005079 public void runAfterAnimationFinished(Runnable runnable) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005080 mAnimationFinishedRunnables.add(runnable);
5081 }
5082
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005083 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
yoshiki iguchi4e30e762018-02-06 12:09:23 +09005084 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005085 mHeadsUpManager = headsUpManager;
Selim Cinek29aab962018-02-27 17:05:45 -08005086 mHeadsUpManager.addListener(mRoundnessManager);
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04005087 mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005088 }
5089
Ned Burnsf81c4c42019-01-07 14:10:43 -05005090 public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
Evan Laird94492852018-10-25 13:43:01 -04005091 ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
5092 generateHeadsUpAnimation(row, isHeadsUp);
5093 }
5094
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005095 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005096 public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07005097 if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005098 mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
5099 mNeedsAnimation = true;
Selim Cinek73cf02a2016-06-17 13:08:00 -07005100 if (!mIsExpanded && !isHeadsUp) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07005101 row.setHeadsUpAnimatingAway(true);
Selim Cinek73cf02a2016-06-17 13:08:00 -07005102 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005103 requestChildrenUpdate();
5104 }
5105 }
5106
Selim Cineka59ecc32015-04-07 10:51:49 -07005107 /**
5108 * Set the boundary for the bottom heads up position. The heads up will always be above this
5109 * position.
5110 *
Jason Monke59dc402018-08-16 12:05:01 -04005111 * @param height the height of the screen
Selim Cineka59ecc32015-04-07 10:51:49 -07005112 * @param bottomBarHeight the height of the bar on the bottom
5113 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005114 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cineka59ecc32015-04-07 10:51:49 -07005115 public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
5116 mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
5117 mStateAnimator.setHeadsUpAppearHeightBottom(height);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005118 requestChildrenUpdate();
5119 }
5120
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005121 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005122 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
5123 mTrackingHeadsUp = row != null;
5124 mRoundnessManager.setTrackingHeadsUp(row);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005125 }
5126
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005127 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaac93252015-04-14 20:04:12 -07005128 public void setScrimController(ScrimController scrimController) {
5129 mScrimController = scrimController;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -07005130 mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
Selim Cinekaac93252015-04-14 20:04:12 -07005131 }
5132
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005133 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005134 public void forceNoOverlappingRendering(boolean force) {
5135 mForceNoOverlappingRendering = force;
5136 }
5137
5138 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005139 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbbc580b2015-06-03 14:11:03 +02005140 public boolean hasOverlappingRendering() {
5141 return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
5142 }
5143
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005144 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek6811d722016-01-19 17:53:12 -08005145 public void setAnimationRunning(boolean animationRunning) {
5146 if (animationRunning != mAnimationRunning) {
5147 if (animationRunning) {
Selim Cinekc383fd02016-10-21 15:31:26 -07005148 getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005149 } else {
Selim Cinekc383fd02016-10-21 15:31:26 -07005150 getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
Selim Cinek6811d722016-01-19 17:53:12 -08005151 }
5152 mAnimationRunning = animationRunning;
Selim Cinek33223572016-02-19 19:32:22 -08005153 updateContinuousShadowDrawing();
Selim Cinek6811d722016-01-19 17:53:12 -08005154 }
5155 }
5156
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005157 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3776fe02016-02-04 13:32:43 -08005158 public boolean isExpanded() {
5159 return mIsExpanded;
5160 }
5161
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005162 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin4e023812018-04-02 21:19:23 -07005163 public void setPulsing(boolean pulsing, boolean animated) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +09005164 if (!mPulsing && !pulsing) {
Adrian Roosb2a87292017-02-13 15:05:03 +01005165 return;
5166 }
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005167 mPulsing = pulsing;
Selim Cinekebf42342017-07-13 15:46:10 +02005168 mAmbientState.setPulsing(pulsing);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005169 updateNotificationAnimationStates();
Lucas Dupin6bf7b642018-01-22 18:56:24 -08005170 updateAlgorithmHeightAndPadding();
Adrian Roos7a9551a2017-01-11 12:27:49 -08005171 updateContentHeight();
Adrian Roosd83e9992017-03-16 15:17:57 -07005172 requestChildrenUpdate();
Lucas Dupin4e023812018-04-02 21:19:23 -07005173 notifyHeightChangeListener(null, animated);
Selim Cinekcd5b22f2016-03-08 16:15:41 -08005174 }
5175
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005176 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekbc243a92016-09-27 16:35:13 -07005177 public void setQsExpanded(boolean qsExpanded) {
5178 mQsExpanded = qsExpanded;
5179 updateAlgorithmLayoutMinHeight();
Riddle Hsu065c01c2018-05-10 23:14:19 +08005180 updateScrollability();
Selim Cinekbc243a92016-09-27 16:35:13 -07005181 }
5182
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005183 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005184 public void setQsExpansionFraction(float qsExpansionFraction) {
5185 mQsExpansionFraction = qsExpansionFraction;
5186 }
5187
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005188 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Gus Prevas0fa58d62019-01-11 13:58:40 -05005189 private void setOwnScrollY(int ownScrollY) {
5190 assert !ANCHOR_SCROLLING;
Selim Cinekef406062016-09-29 17:33:13 -07005191 if (ownScrollY != mOwnScrollY) {
Selim Cinek9212de82017-02-06 16:04:28 -08005192 // We still want to call the normal scrolled changed for accessibility reasons
5193 onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
Selim Cinekef406062016-09-29 17:33:13 -07005194 mOwnScrollY = ownScrollY;
Gus Prevas0fa58d62019-01-11 13:58:40 -05005195 updateOnScrollChange();
5196 }
5197 }
5198
5199 private void updateOnScrollChange() {
5200 updateForwardAndBackwardScrollability();
5201 requestChildrenUpdate();
5202 }
5203
5204 private void updateScrollAnchor() {
5205 int anchorIndex = indexOfChild(mScrollAnchorView);
5206 // If the anchor view has been scrolled off the top, move to the next view.
5207 while (mScrollAnchorViewY < 0) {
5208 View nextAnchor = null;
5209 for (int i = anchorIndex + 1; i < getChildCount(); i++) {
5210 View child = getChildAt(i);
5211 if (child.getVisibility() != View.GONE
5212 && child instanceof ExpandableNotificationRow) {
5213 anchorIndex = i;
5214 nextAnchor = child;
5215 break;
5216 }
5217 }
5218 if (nextAnchor == null) {
5219 break;
5220 }
5221 mScrollAnchorViewY +=
5222 (int) (nextAnchor.getTranslationY() - mScrollAnchorView.getTranslationY());
5223 mScrollAnchorView = nextAnchor;
5224 }
5225 // If the view above the anchor view is fully visible, make it the anchor view.
5226 while (anchorIndex > 0 && mScrollAnchorViewY > 0) {
5227 View prevAnchor = null;
5228 for (int i = anchorIndex - 1; i >= 0; i--) {
5229 View child = getChildAt(i);
5230 if (child.getVisibility() != View.GONE
5231 && child instanceof ExpandableNotificationRow) {
5232 anchorIndex = i;
5233 prevAnchor = child;
5234 break;
5235 }
5236 }
5237 if (prevAnchor == null) {
5238 break;
5239 }
5240 float distanceToPreviousAnchor =
5241 mScrollAnchorView.getTranslationY() - prevAnchor.getTranslationY();
5242 if (distanceToPreviousAnchor < mScrollAnchorViewY) {
5243 mScrollAnchorViewY -= (int) distanceToPreviousAnchor;
5244 mScrollAnchorView = prevAnchor;
5245 }
Selim Cinekef406062016-09-29 17:33:13 -07005246 }
5247 }
5248
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005249 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005250 public void setShelf(NotificationShelf shelf) {
Selim Cinek281c2022016-10-13 19:14:43 -07005251 int index = -1;
5252 if (mShelf != null) {
5253 index = indexOfChild(mShelf);
5254 removeView(mShelf);
5255 }
Selim Cinek0e8d77e2016-11-29 10:35:42 -08005256 mShelf = shelf;
Selim Cinek281c2022016-10-13 19:14:43 -07005257 addView(mShelf, index);
5258 mAmbientState.setShelf(shelf);
Selim Cinekeccb5de2016-10-28 15:04:05 -07005259 mStateAnimator.setShelf(shelf);
Selim Cinekc383fd02016-10-21 15:31:26 -07005260 shelf.bind(mAmbientState, this);
Gus Prevas0fa58d62019-01-11 13:58:40 -05005261 if (ANCHOR_SCROLLING) {
5262 mScrollAnchorView = mShelf;
5263 }
Selim Cinek281c2022016-10-13 19:14:43 -07005264 }
5265
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005266 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek281c2022016-10-13 19:14:43 -07005267 public NotificationShelf getNotificationShelf() {
5268 return mShelf;
5269 }
5270
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005271 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekad7fac02016-10-18 17:09:15 -07005272 public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
5273 if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
5274 mMaxDisplayedNotifications = maxDisplayedNotifications;
5275 updateContentHeight();
5276 notifyHeightChangeListener(mShelf);
5277 }
5278 }
5279
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005280 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
shawnlin8e4e92c2018-04-12 18:47:24 +08005281 public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
Jason Monke59dc402018-08-16 12:05:01 -04005282 mShouldShowShelfOnly = shouldShowShelfOnly;
shawnlin8e4e92c2018-04-12 18:47:24 +08005283 updateAlgorithmLayoutMinHeight();
5284 }
5285
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005286 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinek48ff9b42016-11-09 19:31:51 -08005287 public int getMinExpansionHeight() {
Selim Cinekd127d792016-11-01 19:11:41 -07005288 return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
Selim Cinek48ff9b42016-11-09 19:31:51 -08005289 }
5290
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005291 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005292 public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
5293 mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
5294 updateClipping();
5295 }
5296
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005297 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekcafa87f2016-10-26 17:00:17 -07005298 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
5299 mHeadsUpAnimatingAway = headsUpAnimatingAway;
5300 updateClipping();
5301 }
5302
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005303 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monk297c04e2018-08-23 17:16:59 -04005304 @VisibleForTesting
5305 protected void setStatusBarState(int statusBarState) {
Selim Cinek355652a2016-12-07 13:32:12 -08005306 mStatusBarState = statusBarState;
5307 mAmbientState.setStatusBarState(statusBarState);
Evan Laird91d0f102018-09-18 17:39:55 -04005308 }
5309
5310 private void onStatePostChange() {
Jason Monk1fd3fc32018-08-14 17:20:09 -04005311 boolean onKeyguard = onKeyguard();
5312 boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
Evan Laird91d0f102018-09-18 17:39:55 -04005313
Jason Monk1fd3fc32018-08-14 17:20:09 -04005314 if (mHeadsUpAppearanceController != null) {
5315 mHeadsUpAppearanceController.setPublicMode(publicMode);
5316 }
5317
Beverly8fdb5332019-02-04 14:29:49 -05005318 SysuiStatusBarStateController state = (SysuiStatusBarStateController)
5319 Dependency.get(StatusBarStateController.class);
Jason Monk1fd3fc32018-08-14 17:20:09 -04005320 setHideSensitive(publicMode, state.goingToFullShade() /* animate */);
5321 setDimmed(onKeyguard, state.fromShadeLocked() /* animate */);
5322 setExpandingEnabled(!onKeyguard);
5323 ActivatableNotificationView activatedChild = getActivatedChild();
5324 setActivatedChild(null);
5325 if (activatedChild != null) {
5326 activatedChild.makeInactive(false /* animate */);
5327 }
Jason Monke59dc402018-08-16 12:05:01 -04005328 updateFooter();
Dave Mankoff57445802018-10-10 14:47:34 -04005329 requestChildrenUpdate();
Jason Monke59dc402018-08-16 12:05:01 -04005330 onUpdateRowStates();
Evan Laird91d0f102018-09-18 17:39:55 -04005331
5332 mEntryManager.updateNotifications();
Selim Cinek355652a2016-12-07 13:32:12 -08005333 }
5334
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005335 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekd5ab6452016-12-08 16:34:00 -08005336 public void setExpandingVelocity(float expandingVelocity) {
5337 mAmbientState.setExpandingVelocity(expandingVelocity);
5338 }
5339
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005340 @ShadeViewRefactor(RefactorComponent.COORDINATOR)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005341 public float getOpeningHeight() {
5342 if (mEmptyShadeView.getVisibility() == GONE) {
5343 return getMinExpansionHeight();
5344 } else {
5345 return getAppearEndPosition();
5346 }
5347 }
5348
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005349 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekfcff4c62016-12-27 14:26:06 +01005350 public void setIsFullWidth(boolean isFullWidth) {
5351 mAmbientState.setPanelFullWidth(isFullWidth);
5352 }
5353
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005354 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekec29d342017-05-05 18:31:49 -07005355 public void setUnlockHintRunning(boolean running) {
5356 mAmbientState.setUnlockHintRunning(running);
5357 }
5358
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005359 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005360 public void setQsCustomizerShowing(boolean isShowing) {
5361 mAmbientState.setQsCustomizerShowing(isShowing);
5362 requestChildrenUpdate();
5363 }
5364
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005365 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek5cf1d052017-06-01 17:36:46 -07005366 public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
5367 mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
5368 }
5369
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005370 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin0cd882f2018-01-30 12:19:49 -08005371 public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
5372 mAntiBurnInOffsetX = antiBurnInOffsetX;
Bill Line60aa1e2018-06-13 18:07:15 +08005373 updatePanelTranslation();
Adrian Roosdc747bd2017-06-01 16:09:15 -07005374 }
5375
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005376 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek707e2072017-06-30 18:32:40 +02005377 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5378 pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
shawnlin8e4e92c2018-04-12 18:47:24 +08005379 + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
5380 + " qsExpandFraction=%f]",
Selim Cinek707e2072017-06-30 18:32:40 +02005381 this.getClass().getSimpleName(),
Jason Monke59dc402018-08-16 12:05:01 -04005382 mPulsing ? "T" : "f",
5383 mAmbientState.isQsCustomizerShowing() ? "T" : "f",
Selim Cinek707e2072017-06-30 18:32:40 +02005384 getVisibility() == View.VISIBLE ? "visible"
5385 : getVisibility() == View.GONE ? "gone"
5386 : "invisible",
5387 getAlpha(),
shawnlin8e4e92c2018-04-12 18:47:24 +08005388 mAmbientState.getScrollY(),
5389 mMaxTopPadding,
Jason Monke59dc402018-08-16 12:05:01 -04005390 mShouldShowShelfOnly ? "T" : "f",
shawnlin8e4e92c2018-04-12 18:47:24 +08005391 mQsExpansionFraction));
Selim Cinek30887662018-10-15 17:37:21 -07005392 int childCount = getChildCount();
5393 pw.println(" Number of children: " + childCount);
5394 pw.println();
5395
5396 for (int i = 0; i < childCount; i++) {
5397 ExpandableView child = (ExpandableView) getChildAt(i);
5398 child.dump(fd, pw, args);
5399 if (!(child instanceof ExpandableNotificationRow)) {
5400 pw.println(" " + child.getClass().getSimpleName());
5401 // Notifications dump it's viewstate as part of their dump to support children
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005402 ExpandableViewState viewState = child.getViewState();
Selim Cinek30887662018-10-15 17:37:21 -07005403 if (viewState == null) {
5404 pw.println(" no viewState!!!");
5405 } else {
5406 pw.print(" ");
5407 viewState.dump(fd, pw, args);
5408 pw.println();
5409 pw.println();
5410 }
5411 }
5412 }
Selim Cinek30887662018-10-15 17:37:21 -07005413 int transientViewCount = getTransientViewCount();
Selim Cinekd4c32302018-11-19 19:43:14 -08005414 pw.println(" Transient Views: " + transientViewCount);
Selim Cinek30887662018-10-15 17:37:21 -07005415 for (int i = 0; i < transientViewCount; i++) {
5416 ExpandableView child = (ExpandableView) getTransientView(i);
5417 child.dump(fd, pw, args);
5418 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -05005419 ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
Selim Cinekd4c32302018-11-19 19:43:14 -08005420 int draggedCount = draggedViews.size();
5421 pw.println(" Dragged Views: " + draggedCount);
5422 for (int i = 0; i < draggedCount; i++) {
5423 ExpandableView child = (ExpandableView) draggedViews.get(i);
5424 child.dump(fd, pw, args);
5425 }
Selim Cinek707e2072017-06-30 18:32:40 +02005426 }
5427
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005428 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Lucas Dupin16cfe452018-02-08 13:14:50 -08005429 public boolean isFullyDark() {
5430 return mAmbientState.isFullyDark();
5431 }
5432
Selim Cinek7103fd42016-05-09 22:22:33 -04005433 /**
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005434 * Add a listener whenever the expanded height changes. The first value passed as an argument
5435 * is the expanded height and the second one is the appearFraction.
5436 *
5437 * @param listener the listener to notify.
5438 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005439 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekaa9db1f2018-02-27 17:35:47 -08005440 public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
5441 mExpandedHeightListeners.add(listener);
5442 }
5443
5444 /**
Selim Cinek60ffea62018-03-22 13:16:44 -07005445 * Stop a listener from listening to the expandedHeight.
5446 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005447 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek60ffea62018-03-22 13:16:44 -07005448 public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
5449 mExpandedHeightListeners.remove(listener);
5450 }
5451
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005452 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinekf0c79e12018-05-14 17:17:31 -07005453 public void setHeadsUpAppearanceController(
5454 HeadsUpAppearanceController headsUpAppearanceController) {
5455 mHeadsUpAppearanceController = headsUpAppearanceController;
5456 }
5457
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005458 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek9bfc7a52018-06-11 16:09:00 -07005459 public void setIconAreaController(NotificationIconAreaController controller) {
5460 mIconAreaController = controller;
5461 }
5462
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005463 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005464 public void manageNotifications(View v) {
5465 Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
5466 mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
5467 }
5468
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005469 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -04005470 private void clearNotifications(
5471 @SelectedRows int selection,
5472 boolean closeShade) {
Jason Monke59dc402018-08-16 12:05:01 -04005473 // animate-swipe all dismissable notifications, then animate the shade closed
5474 int numChildren = getChildCount();
5475
5476 final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
5477 final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
5478 for (int i = 0; i < numChildren; i++) {
5479 final View child = getChildAt(i);
5480 if (child instanceof ExpandableNotificationRow) {
5481 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
5482 boolean parentVisible = false;
5483 boolean hasClipBounds = child.getClipBounds(mTmpRect);
Ned Burns61269442019-05-02 18:27:23 -04005484 if (includeChildInDismissAll(row, selection)) {
Jason Monke59dc402018-08-16 12:05:01 -04005485 viewsToRemove.add(row);
5486 if (child.getVisibility() == View.VISIBLE
5487 && (!hasClipBounds || mTmpRect.height() > 0)) {
5488 viewsToHide.add(child);
5489 parentVisible = true;
5490 }
5491 } else if (child.getVisibility() == View.VISIBLE
5492 && (!hasClipBounds || mTmpRect.height() > 0)) {
5493 parentVisible = true;
5494 }
5495 List<ExpandableNotificationRow> children = row.getNotificationChildren();
5496 if (children != null) {
5497 for (ExpandableNotificationRow childRow : children) {
Ned Burns61269442019-05-02 18:27:23 -04005498 if (includeChildInDismissAll(row, selection)) {
5499 viewsToRemove.add(childRow);
5500 if (parentVisible && row.areChildrenExpanded()) {
5501 hasClipBounds = childRow.getClipBounds(mTmpRect);
5502 if (childRow.getVisibility() == View.VISIBLE
5503 && (!hasClipBounds || mTmpRect.height() > 0)) {
5504 viewsToHide.add(childRow);
5505 }
Jason Monke59dc402018-08-16 12:05:01 -04005506 }
5507 }
5508 }
5509 }
5510 }
5511 }
Ned Burns61269442019-05-02 18:27:23 -04005512
Jason Monke59dc402018-08-16 12:05:01 -04005513 if (viewsToRemove.isEmpty()) {
Ned Burns61269442019-05-02 18:27:23 -04005514 if (closeShade) {
5515 mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
5516 }
Jason Monke59dc402018-08-16 12:05:01 -04005517 return;
5518 }
5519
Ned Burns61269442019-05-02 18:27:23 -04005520 performDismissAllAnimations(viewsToHide, closeShade, () -> {
Jason Monke59dc402018-08-16 12:05:01 -04005521 for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
Ned Burns61269442019-05-02 18:27:23 -04005522 if (StackScrollAlgorithm.canChildBeDismissed(rowToRemove)) {
5523 if (selection == ROWS_ALL) {
5524 // TODO: This is a listener method; we shouldn't be calling it. Can we just
5525 // call performRemoveNotification as below?
5526 mEntryManager.removeNotification(
5527 rowToRemove.getEntry().key,
5528 null /* ranking */,
5529 NotificationListenerService.REASON_CANCEL_ALL);
5530 } else {
5531 mEntryManager.performRemoveNotification(
5532 rowToRemove.getEntry().notification,
5533 NotificationListenerService.REASON_CANCEL_ALL);
5534 }
Jason Monke59dc402018-08-16 12:05:01 -04005535 } else {
5536 rowToRemove.resetTranslation();
5537 }
5538 }
Ned Burns61269442019-05-02 18:27:23 -04005539 if (selection == ROWS_ALL) {
5540 try {
5541 mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
5542 } catch (Exception ex) {
5543 }
Jason Monke59dc402018-08-16 12:05:01 -04005544 }
5545 });
Jason Monke59dc402018-08-16 12:05:01 -04005546 }
5547
Ned Burns61269442019-05-02 18:27:23 -04005548 private boolean includeChildInDismissAll(
5549 ExpandableNotificationRow row,
5550 @SelectedRows int selection) {
5551 return StackScrollAlgorithm.canChildBeDismissed(row) && matchesSelection(row, selection);
5552 }
5553
5554 /**
5555 * Given a list of rows, animates them away in a staggered fashion as if they were dismissed.
5556 * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete
5557 * handler.
5558 *
5559 * @param hideAnimatedList List of rows to animated away. Should only be views that are
5560 * currently visible, or else the stagger will look funky.
5561 * @param closeShade Whether to close the shade after the stagger animation completes.
5562 * @param onAnimationComplete Called after the entire animation completes (including the shade
5563 * closing if appropriate). The rows must be dismissed for real here.
5564 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005565 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Ned Burns61269442019-05-02 18:27:23 -04005566 private void performDismissAllAnimations(
5567 final ArrayList<View> hideAnimatedList,
5568 final boolean closeShade,
5569 final Runnable onAnimationComplete) {
5570
5571 final Runnable onSlideAwayAnimationComplete = () -> {
5572 if (closeShade) {
5573 mShadeController.addPostCollapseAction(() -> {
5574 setDismissAllInProgress(false);
5575 onAnimationComplete.run();
5576 });
5577 mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
5578 } else {
5579 setDismissAllInProgress(false);
5580 onAnimationComplete.run();
5581 }
Jason Monke59dc402018-08-16 12:05:01 -04005582 };
5583
5584 if (hideAnimatedList.isEmpty()) {
Ned Burns61269442019-05-02 18:27:23 -04005585 onSlideAwayAnimationComplete.run();
Jason Monke59dc402018-08-16 12:05:01 -04005586 return;
5587 }
5588
5589 // let's disable our normal animations
5590 setDismissAllInProgress(true);
5591
5592 // Decrease the delay for every row we animate to give the sense of
5593 // accelerating the swipes
5594 int rowDelayDecrement = 10;
5595 int currentDelay = 140;
5596 int totalDelay = 180;
5597 int numItems = hideAnimatedList.size();
5598 for (int i = numItems - 1; i >= 0; i--) {
5599 View view = hideAnimatedList.get(i);
5600 Runnable endRunnable = null;
5601 if (i == 0) {
Ned Burns61269442019-05-02 18:27:23 -04005602 endRunnable = onSlideAwayAnimationComplete;
Jason Monke59dc402018-08-16 12:05:01 -04005603 }
Lucas Dupinfb8bdbb2018-12-02 15:09:37 -08005604 dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
Jason Monke59dc402018-08-16 12:05:01 -04005605 currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
5606 totalDelay += currentDelay;
5607 }
5608 }
5609
5610 @VisibleForTesting
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005611 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jason Monke59dc402018-08-16 12:05:01 -04005612 protected void inflateFooterView() {
5613 FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
5614 R.layout.status_bar_notification_footer, this, false);
5615 footerView.setDismissButtonClickListener(v -> {
5616 mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
Ned Burns61269442019-05-02 18:27:23 -04005617 clearNotifications(ROWS_ALL, true /* closeShade */);
Jason Monke59dc402018-08-16 12:05:01 -04005618 });
5619 footerView.setManageButtonClickListener(this::manageNotifications);
5620 setFooterView(footerView);
5621 }
5622
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005623 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5624 private void inflateEmptyShadeView() {
Jason Monke59dc402018-08-16 12:05:01 -04005625 EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
5626 R.layout.status_bar_no_notifications, this, false);
5627 view.setText(R.string.empty_shade_text);
5628 setEmptyShadeView(view);
5629 }
5630
5631 /**
5632 * Updates expanded, dimmed and locked states of notification rows.
5633 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005634 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Jason Monke59dc402018-08-16 12:05:01 -04005635 public void onUpdateRowStates() {
5636 changeViewPosition(mFooterView, -1);
5637
5638 // The following views will be moved to the end of mStackScroller. This counter represents
5639 // the offset from the last child. Initialized to 1 for the very last position. It is post-
5640 // incremented in the following "changeViewPosition" calls so that its value is correct for
5641 // subsequent calls.
5642 int offsetFromEnd = 1;
5643 changeViewPosition(mEmptyShadeView,
5644 getChildCount() - offsetFromEnd++);
5645
5646 // No post-increment for this call because it is the last one. Make sure to add one if
5647 // another "changeViewPosition" call is ever added.
5648 changeViewPosition(mShelf,
5649 getChildCount() - offsetFromEnd);
Jason Monke59dc402018-08-16 12:05:01 -04005650 }
5651
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005652 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5653 public void setNotificationPanel(NotificationPanelView notificationPanelView) {
Jason Monke59dc402018-08-16 12:05:01 -04005654 mNotificationPanel = notificationPanelView;
5655 }
5656
Jason Monk297c04e2018-08-23 17:16:59 -04005657 public void updateIconAreaViews() {
5658 mIconAreaController.updateNotificationIcons();
5659 }
5660
Selim Cinek60ffea62018-03-22 13:16:44 -07005661 /**
Selim Cinek3d6ae232019-01-04 14:14:33 -08005662 * Set how far the wake up is when waking up from pulsing. This is a height and will adjust the
5663 * notification positions accordingly.
5664 * @param height the new wake up height
5665 * @return the overflow how much the height is further than he lowest notification
5666 */
Selim Cinek5040f2e2019-02-14 18:22:42 -08005667 public float setPulseHeight(float height) {
5668 mAmbientState.setPulseHeight(height);
Selim Cinek3d6ae232019-01-04 14:14:33 -08005669 requestChildrenUpdate();
5670 return Math.max(0, height - mAmbientState.getInnerHeight(true /* ignorePulseHeight */));
5671 }
5672
5673 /**
5674 * Set the amount how much we're dozing. This is different from how dark the shade is, when
5675 * the notification is pulsing.
5676 */
5677 public void setDozeAmount(float dozeAmount) {
5678 mAmbientState.setDozeAmount(dozeAmount);
Selim Cinekae55d832019-02-22 17:43:43 -08005679 updateContinuousBackgroundDrawing();
Selim Cinek3d6ae232019-01-04 14:14:33 -08005680 requestChildrenUpdate();
5681 }
5682
Selim Cinek459aee32019-02-20 11:18:56 -08005683 public void wakeUpFromPulse() {
Selim Cinek34518f62019-02-28 19:41:18 -08005684 setPulseHeight(getPulseHeight());
Selim Cinek459aee32019-02-20 11:18:56 -08005685 // Let's place the hidden views at the end of the pulsing notification to make sure we have
5686 // a smooth animation
5687 boolean firstVisibleView = true;
5688 float wakeUplocation = -1f;
5689 int childCount = getChildCount();
5690 for (int i = 0; i < childCount; i++) {
5691 ExpandableView view = (ExpandableView) getChildAt(i);
5692 if (view.getVisibility() == View.GONE) {
5693 continue;
5694 }
5695 boolean isShelf = view == mShelf;
5696 if (!(view instanceof ExpandableNotificationRow) && !isShelf) {
5697 continue;
5698 }
5699 if (view.getVisibility() == View.VISIBLE && !isShelf) {
5700 if (firstVisibleView) {
5701 firstVisibleView = false;
5702 wakeUplocation = view.getTranslationY()
5703 + view.getActualHeight() - mShelf.getIntrinsicHeight();
5704 }
5705 } else if (!firstVisibleView) {
5706 view.setTranslationY(wakeUplocation);
5707 }
5708 }
5709 }
5710
Selim Cinek6f0a62a2019-04-09 18:40:12 -07005711 @Override
5712 public void onDynamicPrivacyChanged() {
5713 if (mIsExpanded) {
5714 // The bottom might change because we're using the final actual height of the view
5715 mAnimateBottomOnLayout = true;
5716 }
5717 }
5718
Selim Cinek3d6ae232019-01-04 14:14:33 -08005719 /**
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005720 * A listener that is notified when the empty space below the notifications is clicked on
5721 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005722 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005723 public interface OnEmptySpaceClickListener {
Anthony Chen3cb3ad92016-12-01 10:58:47 -08005724 void onEmptySpaceClicked(float x, float y);
Selim Cinek3a9c10a2014-10-28 14:21:10 +01005725 }
5726
5727 /**
Jorim Jaggi290600a2014-05-30 17:02:20 +02005728 * A listener that gets notified when the overscroll at the top has changed.
5729 */
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005730 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Jorim Jaggi290600a2014-05-30 17:02:20 +02005731 public interface OnOverscrollTopChangedListener {
Jorim Jaggi475b21d2014-07-01 18:13:24 +02005732
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005733 /**
5734 * Notifies a listener that the overscroll has changed.
5735 *
5736 * @param amount the amount of overscroll, in pixels
5737 * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
5738 * unrubberbanded motion to directly expand overscroll view (e.g
5739 * expand
5740 * QS)
5741 */
5742 void onOverscrollTopChanged(float amount, boolean isRubberbanded);
Selim Cinek1408eb52014-06-02 14:45:38 +02005743
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005744 /**
5745 * Notify a listener that the scroller wants to escape from the scrolling motion and
5746 * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
5747 *
5748 * @param velocity The velocity that the Scroller had when over flinging
5749 * @param open Should the fling open or close the overscroll view.
5750 */
5751 void flingTopOverscroll(float velocity, boolean open);
5752 }
Jorim Jaggi290600a2014-05-30 17:02:20 +02005753
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005754 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
5755 public boolean hasActiveNotifications() {
Jason Monke59dc402018-08-16 12:05:01 -04005756 return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
5757 }
5758
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04005759 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005760 public void updateSpeedBumpIndex() {
Jason Monke59dc402018-08-16 12:05:01 -04005761 int speedBumpIndex = 0;
5762 int currentIndex = 0;
5763 final int N = getChildCount();
5764 for (int i = 0; i < N; i++) {
5765 View view = getChildAt(i);
5766 if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
5767 continue;
5768 }
5769 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
5770 currentIndex++;
Gus Prevas33619af2018-10-26 15:40:27 -04005771 boolean beforeSpeedBump;
5772 if (mLowPriorityBeforeSpeedBump) {
Gus Prevascaed15c2019-01-18 14:19:51 -05005773 beforeSpeedBump = !row.getEntry().ambient;
Gus Prevas33619af2018-10-26 15:40:27 -04005774 } else {
Gus Prevascaed15c2019-01-18 14:19:51 -05005775 beforeSpeedBump = row.getEntry().isHighPriority();
Gus Prevas33619af2018-10-26 15:40:27 -04005776 }
5777 if (beforeSpeedBump) {
Jason Monke59dc402018-08-16 12:05:01 -04005778 speedBumpIndex = currentIndex;
5779 }
5780 }
5781 boolean noAmbient = speedBumpIndex == N;
5782 updateSpeedBumpIndex(speedBumpIndex, noAmbient);
5783 }
5784
Gus Prevase2d6f042018-10-17 15:25:30 -04005785 /** Updates the indices of the boundaries between sections. */
5786 @ShadeViewRefactor(RefactorComponent.INPUT)
5787 public void updateSectionBoundaries() {
Ned Burns9eb06332019-04-23 16:02:12 -04005788 mSectionsManager.updateSectionBoundaries();
Gus Prevase2d6f042018-10-17 15:25:30 -04005789 }
5790
Selim Cinekae55d832019-02-22 17:43:43 -08005791 private void updateContinuousBackgroundDrawing() {
5792 boolean continuousBackground = !mAmbientState.isFullyAwake()
5793 && !mAmbientState.getDraggedViews().isEmpty();
5794 if (continuousBackground != mContinuousBackgroundUpdate) {
5795 mContinuousBackgroundUpdate = continuousBackground;
5796 if (continuousBackground) {
5797 getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater);
5798 } else {
5799 getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater);
5800 }
5801 }
5802 }
5803
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005804 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
Selim Cinek33223572016-02-19 19:32:22 -08005805 private void updateContinuousShadowDrawing() {
5806 boolean continuousShadowUpdate = mAnimationRunning
5807 || !mAmbientState.getDraggedViews().isEmpty();
5808 if (continuousShadowUpdate != mContinuousShadowUpdate) {
5809 if (continuousShadowUpdate) {
5810 getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
5811 } else {
5812 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
5813 }
Jorim Jaggi38b5ec92016-04-12 01:39:49 -07005814 mContinuousShadowUpdate = continuousShadowUpdate;
Selim Cinek33223572016-02-19 19:32:22 -08005815 }
5816 }
5817
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09005818 @Override
Aaron Heuckroth441b7dd2018-08-24 15:52:49 -04005819 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
Mady Mellor95d743c2017-01-10 12:05:27 -08005820 public void resetExposedMenuView(boolean animate, boolean force) {
5821 mSwipeHelper.resetExposedMenuView(animate, force);
Mady Mellor7a9b2a62016-03-23 07:41:47 -07005822 }
5823
Ned Burns61269442019-05-02 18:27:23 -04005824 private static boolean matchesSelection(
5825 ExpandableNotificationRow row,
5826 @SelectedRows int selection) {
5827 switch (selection) {
5828 case ROWS_ALL:
5829 return true;
5830 case ROWS_HIGH_PRIORITY:
5831 return row.getEntry().isHighPriority();
5832 case ROWS_GENTLE:
5833 return !row.getEntry().isHighPriority();
5834 default:
5835 throw new IllegalArgumentException("Unknown selection: " + selection);
5836 }
5837 }
5838
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04005839 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
5840 static class AnimationEvent {
Selim Cinek572bbd42014-04-25 16:43:27 +02005841
Jason Monke59dc402018-08-16 12:05:01 -04005842 static AnimationFilter[] FILTERS = new AnimationFilter[]{
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005843
5844 // ANIMATION_TYPE_ADD
5845 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005846 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005847 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005848 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005849 .animateZ()
5850 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005851
5852 // ANIMATION_TYPE_REMOVE
5853 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005854 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005855 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005856 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005857 .animateZ()
5858 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005859
5860 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5861 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005862 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005863 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005864 .animateY()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005865 .animateZ()
5866 .hasDelays(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005867
5868 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5869 new AnimationFilter()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005870 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005871 .animateTopInset()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005872 .animateY()
5873 .animateDimmed()
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005874 .animateZ(),
5875
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005876 // ANIMATION_TYPE_ACTIVATED_CHILD
5877 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005878 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005879
5880 // ANIMATION_TYPE_DIMMED
5881 new AnimationFilter()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005882 .animateDimmed(),
5883
5884 // ANIMATION_TYPE_CHANGE_POSITION
5885 new AnimationFilter()
Selim Cinek277a8aa2016-01-22 12:12:37 -08005886 .animateAlpha() // maybe the children change positions
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005887 .animateHeight()
Selim Cinek708a6c12014-05-28 14:16:02 +02005888 .animateTopInset()
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005889 .animateY()
John Spurlockbf370992014-06-17 13:58:31 -04005890 .animateZ(),
5891
5892 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07005893 null, // Unused
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005894
5895 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5896 new AnimationFilter()
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005897 .animateHeight()
5898 .animateTopInset()
5899 .animateY()
5900 .animateDimmed()
Jorim Jaggiae441282014-08-01 02:45:18 +02005901 .animateZ()
5902 .hasDelays(),
5903
5904 // ANIMATION_TYPE_HIDE_SENSITIVE
5905 new AnimationFilter()
5906 .animateHideSensitive(),
Selim Cineka5e211b2014-08-11 17:35:48 +02005907
5908 // ANIMATION_TYPE_VIEW_RESIZE
5909 new AnimationFilter()
Selim Cineka5e211b2014-08-11 17:35:48 +02005910 .animateHeight()
5911 .animateTopInset()
5912 .animateY()
5913 .animateZ(),
Selim Cinekd9acca52014-09-01 22:33:25 +02005914
Selim Cinekb5605e52015-02-20 18:21:41 +01005915 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
5916 new AnimationFilter()
5917 .animateAlpha()
5918 .animateHeight()
5919 .animateTopInset()
5920 .animateY()
5921 .animateZ(),
5922
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005923 // ANIMATION_TYPE_HEADS_UP_APPEAR
5924 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005925 .animateHeight()
5926 .animateTopInset()
5927 .animateY()
5928 .animateZ(),
5929
5930 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
5931 new AnimationFilter()
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005932 .animateHeight()
5933 .animateTopInset()
5934 .animateY()
Selim Cinek332c23f2018-03-16 17:37:50 -07005935 .animateZ()
5936 .hasDelays(),
Selim Cinekb8f09cf2015-03-16 17:09:28 -07005937
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005938 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
5939 new AnimationFilter()
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07005940 .animateHeight()
5941 .animateTopInset()
5942 .animateY()
5943 .animateZ()
5944 .hasDelays(),
5945
Selim Cineka59ecc32015-04-07 10:51:49 -07005946 // ANIMATION_TYPE_HEADS_UP_OTHER
5947 new AnimationFilter()
Selim Cineka59ecc32015-04-07 10:51:49 -07005948 .animateHeight()
5949 .animateTopInset()
5950 .animateY()
5951 .animateZ(),
5952
Selim Cinekd9acca52014-09-01 22:33:25 +02005953 // ANIMATION_TYPE_EVERYTHING
5954 new AnimationFilter()
5955 .animateAlpha()
5956 .animateDark()
Selim Cinekd9acca52014-09-01 22:33:25 +02005957 .animateDimmed()
5958 .animateHideSensitive()
5959 .animateHeight()
5960 .animateTopInset()
5961 .animateY()
5962 .animateZ(),
Jorim Jaggid552d9d2014-05-07 19:41:13 +02005963 };
5964
Jason Monke59dc402018-08-16 12:05:01 -04005965 static int[] LENGTHS = new int[]{
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005966
5967 // ANIMATION_TYPE_ADD
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005968 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005969
5970 // ANIMATION_TYPE_REMOVE
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005971 StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005972
5973 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
5974 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5975
5976 // ANIMATION_TYPE_TOP_PADDING_CHANGED
5977 StackStateAnimator.ANIMATION_DURATION_STANDARD,
5978
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02005979 // ANIMATION_TYPE_ACTIVATED_CHILD
5980 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
5981
5982 // ANIMATION_TYPE_DIMMED
5983 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
Selim Cinek8efa6dd2014-05-19 16:27:37 +02005984
5985 // ANIMATION_TYPE_CHANGE_POSITION
5986 StackStateAnimator.ANIMATION_DURATION_STANDARD,
John Spurlockbf370992014-06-17 13:58:31 -04005987
5988 // ANIMATION_TYPE_DARK
Adrian Roos28f90c72017-05-08 17:24:26 -07005989 StackStateAnimator.ANIMATION_DURATION_WAKEUP,
Jorim Jaggi60d07c52014-07-31 15:38:21 +02005990
5991 // ANIMATION_TYPE_GO_TO_FULL_SHADE
5992 StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
Jorim Jaggiae441282014-08-01 02:45:18 +02005993
5994 // ANIMATION_TYPE_HIDE_SENSITIVE
5995 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cineka5e211b2014-08-11 17:35:48 +02005996
5997 // ANIMATION_TYPE_VIEW_RESIZE
5998 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekd9acca52014-09-01 22:33:25 +02005999
Selim Cinekb5605e52015-02-20 18:21:41 +01006000 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
Selim Cinek99695592016-01-12 17:51:35 -08006001 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Selim Cinekb5605e52015-02-20 18:21:41 +01006002
Selim Cinekb8f09cf2015-03-16 17:09:28 -07006003 // ANIMATION_TYPE_HEADS_UP_APPEAR
6004 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
6005
6006 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
6007 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
6008
Jorim Jaggi5eb67c22015-08-19 19:50:49 -07006009 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
6010 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
6011
Selim Cineka59ecc32015-04-07 10:51:49 -07006012 // ANIMATION_TYPE_HEADS_UP_OTHER
6013 StackStateAnimator.ANIMATION_DURATION_STANDARD,
6014
Selim Cinekd9acca52014-09-01 22:33:25 +02006015 // ANIMATION_TYPE_EVERYTHING
6016 StackStateAnimator.ANIMATION_DURATION_STANDARD,
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006017 };
6018
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006019 static final int ANIMATION_TYPE_ADD = 0;
6020 static final int ANIMATION_TYPE_REMOVE = 1;
6021 static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
6022 static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
Selim Cinekff2ffec2018-11-19 18:52:01 -08006023 static final int ANIMATION_TYPE_ACTIVATED_CHILD = 4;
6024 static final int ANIMATION_TYPE_DIMMED = 5;
6025 static final int ANIMATION_TYPE_CHANGE_POSITION = 6;
6026 static final int ANIMATION_TYPE_DARK = 7;
6027 static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 8;
6028 static final int ANIMATION_TYPE_HIDE_SENSITIVE = 9;
6029 static final int ANIMATION_TYPE_VIEW_RESIZE = 10;
6030 static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 11;
6031 static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 12;
6032 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 13;
6033 static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 14;
6034 static final int ANIMATION_TYPE_HEADS_UP_OTHER = 15;
6035 static final int ANIMATION_TYPE_EVERYTHING = 16;
Jorim Jaggi0dd68812014-05-01 19:17:37 +02006036
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01006037 static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
6038 static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
6039
Selim Cinek572bbd42014-04-25 16:43:27 +02006040 final long eventStartTime;
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006041 final ExpandableView mChangingView;
Selim Cinek572bbd42014-04-25 16:43:27 +02006042 final int animationType;
Jorim Jaggid552d9d2014-05-07 19:41:13 +02006043 final AnimationFilter filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006044 final long length;
Selim Cinek8efa6dd2014-05-19 16:27:37 +02006045 View viewAfterChangingView;
Jorim Jaggi2a5e4522014-11-24 21:45:20 +01006046 int darkAnimationOriginIndex;
Selim Cineka59ecc32015-04-07 10:51:49 -07006047 boolean headsUpFromBottom;
Selim Cinek572bbd42014-04-25 16:43:27 +02006048
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006049 AnimationEvent(ExpandableView view, int type) {
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006050 this(view, type, LENGTHS[type]);
6051 }
6052
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006053 AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006054 this(view, type, LENGTHS[type], filter);
6055 }
6056
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006057 AnimationEvent(ExpandableView view, int type, long length) {
Adrian Roos28f90c72017-05-08 17:24:26 -07006058 this(view, type, length, FILTERS[type]);
6059 }
6060
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006061 AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
Selim Cinek572bbd42014-04-25 16:43:27 +02006062 eventStartTime = AnimationUtils.currentAnimationTimeMillis();
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006063 mChangingView = view;
Selim Cinek572bbd42014-04-25 16:43:27 +02006064 animationType = type;
Jorim Jaggiff9c9c42014-08-01 05:36:22 +02006065 this.length = length;
Adrian Roos28f90c72017-05-08 17:24:26 -07006066 this.filter = filter;
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006067 }
6068
6069 /**
6070 * Combines the length of several animation events into a single value.
6071 *
6072 * @param events The events of the lengths to combine.
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006073 * @return The combined length. Depending on the event types, this might be the maximum of
Jason Monke59dc402018-08-16 12:05:01 -04006074 * all events or the length of a specific event.
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006075 */
6076 static long combineLength(ArrayList<AnimationEvent> events) {
6077 long length = 0;
6078 int size = events.size();
6079 for (int i = 0; i < size; i++) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +02006080 AnimationEvent event = events.get(i);
6081 length = Math.max(length, event.length);
6082 if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
6083 return event.length;
6084 }
Jorim Jaggi5aa045c2014-05-07 21:42:40 +02006085 }
6086 return length;
Selim Cinek572bbd42014-04-25 16:43:27 +02006087 }
6088 }
Jason Monke59dc402018-08-16 12:05:01 -04006089
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006090 @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
6091 private final StateListener mStateListener = new StateListener() {
Jason Monke59dc402018-08-16 12:05:01 -04006092 @Override
6093 public void onStatePreChange(int oldState, int newState) {
6094 if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
6095 requestAnimateEverything();
6096 }
6097 }
6098
6099 @Override
6100 public void onStateChanged(int newState) {
6101 setStatusBarState(newState);
6102 }
Evan Laird91d0f102018-09-18 17:39:55 -04006103
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006104 @Override
6105 public void onStatePostChange() {
Evan Laird91d0f102018-09-18 17:39:55 -04006106 NotificationStackScrollLayout.this.onStatePostChange();
6107 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006108 };
6109
Will Brockmane718d582019-01-17 16:38:38 -05006110 @VisibleForTesting
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006111 @ShadeViewRefactor(RefactorComponent.INPUT)
Will Brockmane718d582019-01-17 16:38:38 -05006112 protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006113 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006114 public void onMenuClicked(View view, int x, int y, MenuItem item) {
6115 if (mLongPressListener == null) {
6116 return;
6117 }
6118 if (view instanceof ExpandableNotificationRow) {
6119 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Will Brockmane718d582019-01-17 16:38:38 -05006120 mMetricsLogger.write(row.getStatusBarNotification().getLogMaker()
6121 .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
6122 .setType(MetricsEvent.TYPE_ACTION)
6123 );
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006124 }
6125 mLongPressListener.onLongPress(view, x, y, item);
6126 }
6127
6128 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006129 public void onMenuReset(View row) {
6130 View translatingParentView = mSwipeHelper.getTranslatingParentView();
6131 if (translatingParentView != null && row == translatingParentView) {
6132 mSwipeHelper.clearExposedMenuView();
6133 mSwipeHelper.clearTranslatingParentView();
Gus Prevas211181532018-12-13 14:49:33 -05006134 if (row instanceof ExpandableNotificationRow) {
6135 mHeadsUpManager.setMenuShown(
6136 ((ExpandableNotificationRow) row).getEntry(), false);
6137
6138 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006139 }
6140 }
6141
6142 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006143 public void onMenuShown(View row) {
6144 if (row instanceof ExpandableNotificationRow) {
Gus Prevas211181532018-12-13 14:49:33 -05006145 ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
Will Brockmane718d582019-01-17 16:38:38 -05006146 mMetricsLogger.write(notificationRow.getStatusBarNotification().getLogMaker()
6147 .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
6148 .setType(MetricsEvent.TYPE_ACTION));
Gus Prevas211181532018-12-13 14:49:33 -05006149 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
Evan Lairde55c6012019-03-13 12:54:37 -04006150 mSwipeHelper.onMenuShown(row);
6151
6152 // Check to see if we want to go directly to the notfication guts
6153 NotificationMenuRowPlugin provider = notificationRow.getProvider();
6154 if (provider.shouldShowGutsOnSnapOpen()) {
6155 MenuItem item = provider.menuItemToExposeOnSnap();
6156 if (item != null) {
6157 Point origin = provider.getRevealAnimationOrigin();
6158 mGutsManager.openGuts(row, origin.x, origin.y, item);
6159 } else {
6160 Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
6161 + "menu item in menuItemtoExposeOnSnap. Skipping.");
6162 }
6163
6164 // Close the menu row since we went directly to the guts
6165 resetExposedMenuView(false, true);
6166 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006167 }
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006168 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006169 };
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006170
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006171 @ShadeViewRefactor(RefactorComponent.INPUT)
6172 private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
6173 new NotificationSwipeHelper.NotificationCallback() {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006174 @Override
6175 public void onDismiss() {
6176 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
6177 false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
6178 false /* resetMenu */);
6179 }
6180
6181 @Override
6182 public void onSnooze(StatusBarNotification sbn,
6183 NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
6184 mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
6185 }
6186
6187 @Override
Selim Cinekae55d832019-02-22 17:43:43 -08006188 public boolean shouldDismissQuickly() {
6189 return NotificationStackScrollLayout.this.isExpanded() && mAmbientState.isFullyAwake();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006190 }
6191
6192 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006193 public void onDragCancelled(View v) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006194 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006195 mFalsingManager.onNotificatonStopDismissing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006196 }
6197
6198 /**
6199 * Handles cleanup after the given {@code view} has been fully swiped out (including
6200 * re-invoking dismiss logic in case the notification has not made its way out yet).
6201 */
6202 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006203 public void onChildDismissed(View view) {
6204 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6205 if (!row.isDismissed()) {
6206 handleChildViewDismissed(view);
6207 }
6208 ViewGroup transientContainer = row.getTransientContainer();
6209 if (transientContainer != null) {
6210 transientContainer.removeTransientView(view);
6211 }
6212 }
6213
6214 /**
6215 * Starts up notification dismiss and tells the notification, if any, to remove itself from
6216 * layout.
6217 *
6218 * @param view view (e.g. notification) to dismiss from the layout
6219 */
6220
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006221 public void handleChildViewDismissed(View view) {
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006222 setSwipingInProgress(false);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006223 if (mDismissAllInProgress) {
6224 return;
6225 }
6226
6227 boolean isBlockingHelperShown = false;
6228
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006229 mAmbientState.onDragFinished(view);
6230 updateContinuousShadowDrawing();
6231
6232 if (view instanceof ExpandableNotificationRow) {
6233 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
6234 if (row.isHeadsUp()) {
6235 mHeadsUpManager.addSwipedOutNotification(
6236 row.getStatusBarNotification().getKey());
6237 }
6238 isBlockingHelperShown =
6239 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
6240 }
6241
6242 if (!isBlockingHelperShown) {
6243 mSwipedOutViews.add(view);
6244 }
6245 mFalsingManager.onNotificationDismissed();
6246 if (mFalsingManager.shouldEnforceBouncer()) {
6247 mStatusBar.executeRunnableDismissingKeyguard(
6248 null,
6249 null /* cancelAction */,
6250 false /* dismissShade */,
6251 true /* afterKeyguardGone */,
6252 false /* deferred */);
6253 }
6254 }
6255
6256 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006257 public boolean isAntiFalsingNeeded() {
6258 return onKeyguard();
6259 }
6260
6261 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006262 public View getChildAtPosition(MotionEvent ev) {
6263 View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
6264 ev.getY());
6265 if (child instanceof ExpandableNotificationRow) {
6266 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
6267 ExpandableNotificationRow parent = row.getNotificationParent();
6268 if (parent != null && parent.areChildrenExpanded()
6269 && (parent.areGutsExposed()
6270 || mSwipeHelper.getExposedMenuView() == parent
6271 || (parent.getNotificationChildren().size() == 1
Evan Laird94492852018-10-25 13:43:01 -04006272 && parent.getEntry().isClearable()))) {
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006273 // In this case the group is expanded and showing the menu for the
6274 // group, further interaction should apply to the group, not any
6275 // child notifications so we use the parent of the child. We also do the same
6276 // if we only have a single child.
6277 child = parent;
6278 }
6279 }
6280 return child;
6281 }
6282
6283 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006284 public void onBeginDrag(View v) {
6285 mFalsingManager.onNotificatonStartDismissing();
Aaron Heuckroth9dc9d4f2018-11-15 11:04:01 -05006286 setSwipingInProgress(true);
Dave Mankoffa4d195d2018-11-16 13:33:27 -05006287 mAmbientState.onBeginDrag((ExpandableView) v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006288 updateContinuousShadowDrawing();
Selim Cinekae55d832019-02-22 17:43:43 -08006289 updateContinuousBackgroundDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006290 requestChildrenUpdate();
6291 }
6292
6293 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006294 public void onChildSnappedBack(View animView, float targetLeft) {
6295 mAmbientState.onDragFinished(animView);
6296 updateContinuousShadowDrawing();
Selim Cinekae55d832019-02-22 17:43:43 -08006297 updateContinuousBackgroundDrawing();
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006298 }
6299
6300 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006301 public boolean updateSwipeProgress(View animView, boolean dismissable,
6302 float swipeProgress) {
6303 // Returning true prevents alpha fading.
6304 return !mFadeNotificationsOnDismiss;
6305 }
6306
6307 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006308 public float getFalsingThresholdFactor() {
6309 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
6310 }
6311
6312 @Override
Gus Prevasc4e68d42019-01-17 15:45:21 -05006313 public int getConstrainSwipeStartPosition() {
6314 NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
6315 if (menuRow != null) {
6316 return Math.abs(menuRow.getMenuSnapTarget());
6317 }
6318 return 0;
6319 }
6320
6321 @Override
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006322 public boolean canChildBeDismissed(View v) {
Ned Burns61269442019-05-02 18:27:23 -04006323 return StackScrollAlgorithm.canChildBeDismissed(v);
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006324 }
Gus Prevas37d67e22018-11-02 14:48:55 -04006325
6326 @Override
6327 public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
Evan Laird30b9b162019-04-24 15:22:24 -04006328 //TODO: b/131242807 for why this doesn't do anything with direction
6329 return canChildBeDismissed(v);
Gus Prevas37d67e22018-11-02 14:48:55 -04006330 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006331 };
6332
6333 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
6334
6335 @ShadeViewRefactor(RefactorComponent.INPUT)
6336 private final DragDownCallback mDragDownCallback = new DragDownCallback() {
6337
6338 /* Only ever called as a consequence of a lockscreen expansion gesture. */
6339 @Override
6340 public boolean onDraggedDown(View startingChild, int dragLengthY) {
6341 if (mStatusBarState == StatusBarState.KEYGUARD
Lucas Dupin55c6e802018-09-27 18:07:36 -07006342 && hasActiveNotifications()) {
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006343 mLockscreenGestureLogger.write(
6344 MetricsEvent.ACTION_LS_SHADE,
6345 (int) (dragLengthY / mDisplayMetrics.density),
6346 0 /* velocityDp - N/A */);
6347
Selim Cinekc1d9ab22019-05-21 18:08:30 -07006348 if (!mAmbientState.isDozing() || startingChild != null) {
Lucas Dupin55c6e802018-09-27 18:07:36 -07006349 // We have notifications, go to locked shade.
Jason Monk297c04e2018-08-23 17:16:59 -04006350 mShadeController.goToLockedShade(startingChild);
Lucas Dupin55c6e802018-09-27 18:07:36 -07006351 if (startingChild instanceof ExpandableNotificationRow) {
6352 ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
6353 row.onExpandedByGesture(true /* drag down is always an open */);
6354 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006355 }
Lucas Dupin55c6e802018-09-27 18:07:36 -07006356
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006357 return true;
6358 } else {
6359 // abort gesture.
6360 return false;
6361 }
6362 }
6363
6364 @Override
6365 public void onDragDownReset() {
6366 setDimmed(true /* dimmed */, true /* animated */);
6367 resetScrollPosition();
6368 resetCheckSnoozeLeavebehind();
6369 }
6370
6371 @Override
6372 public void onCrossedThreshold(boolean above) {
6373 setDimmed(!above /* dimmed */, true /* animate */);
6374 }
6375
6376 @Override
6377 public void onTouchSlopExceeded() {
6378 cancelLongPress();
6379 checkSnoozeLeavebehind();
6380 }
6381
6382 @Override
6383 public void setEmptyDragAmount(float amount) {
6384 mNotificationPanel.setEmptyDragAmount(amount);
6385 }
6386
6387 @Override
6388 public boolean isFalsingCheckNeeded() {
6389 return mStatusBarState == StatusBarState.KEYGUARD;
6390 }
6391 };
6392
6393 public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
6394
6395 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6396 private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
6397 @Override
6398 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6399 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6400 }
6401
6402 @Override
6403 public boolean isExpanded() {
6404 return mIsExpanded;
6405 }
6406
6407 @Override
6408 public Context getContext() {
6409 return mContext;
6410 }
6411 };
6412
6413 public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
6414
6415
6416 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6417 private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
6418 @Override
6419 public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
6420 boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
6421 && (mIsExpanded || changedRow.isPinned());
6422 if (animated) {
6423 mExpandedGroupView = changedRow;
6424 mNeedsAnimation = true;
6425 }
6426 changedRow.setChildrenExpanded(expanded, animated);
6427 if (!mGroupExpandedForMeasure) {
6428 onHeightChanged(changedRow, false /* needsAnimation */);
6429 }
6430 runAfterAnimationFinished(new Runnable() {
6431 @Override
6432 public void run() {
6433 changedRow.onFinishedExpansionChange();
6434 }
6435 });
6436 }
6437
6438 @Override
6439 public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
6440 mStatusBar.requestNotificationUpdate();
6441 }
6442
6443 @Override
6444 public void onGroupsChanged() {
6445 mStatusBar.requestNotificationUpdate();
6446 }
6447 };
6448
6449 @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
6450 private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
6451 @Override
6452 public ExpandableView getChildAtPosition(float touchX, float touchY) {
6453 return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY);
6454 }
6455
6456 @Override
6457 public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
6458 return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
6459 }
6460
6461 @Override
6462 public boolean canChildBeExpanded(View v) {
6463 return v instanceof ExpandableNotificationRow
6464 && ((ExpandableNotificationRow) v).isExpandable()
6465 && !((ExpandableNotificationRow) v).areGutsExposed()
6466 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
6467 }
6468
6469 /* Only ever called as a consequence of an expansion gesture in the shade. */
6470 @Override
6471 public void setUserExpandedChild(View v, boolean userExpanded) {
6472 if (v instanceof ExpandableNotificationRow) {
6473 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
6474 if (userExpanded && onKeyguard()) {
6475 // Due to a race when locking the screen while touching, a notification may be
6476 // expanded even after we went back to keyguard. An example of this happens if
6477 // you click in the empty space while expanding a group.
6478
6479 // We also need to un-user lock it here, since otherwise the content height
6480 // calculated might be wrong. We also can't invert the two calls since
6481 // un-userlocking it will trigger a layout switch in the content view.
6482 row.setUserLocked(false);
6483 updateContentHeight();
6484 notifyHeightChangeListener(row);
6485 return;
6486 }
6487 row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
6488 row.onExpandedByGesture(userExpanded);
6489 }
6490 }
6491
6492 @Override
6493 public void setExpansionCancelled(View v) {
6494 if (v instanceof ExpandableNotificationRow) {
6495 ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
6496 }
6497 }
6498
6499 @Override
6500 public void setUserLockedChild(View v, boolean userLocked) {
6501 if (v instanceof ExpandableNotificationRow) {
6502 ((ExpandableNotificationRow) v).setUserLocked(userLocked);
6503 }
6504 cancelLongPress();
6505 requestDisallowInterceptTouchEvent(true);
6506 }
6507
6508 @Override
6509 public void expansionStateChanged(boolean isExpanding) {
6510 mExpandingNotification = isExpanding;
6511 if (!mExpandedInThisMotion) {
Gus Prevas0fa58d62019-01-11 13:58:40 -05006512 if (ANCHOR_SCROLLING) {
6513 // TODO
6514 } else {
6515 mMaxScrollAfterExpand = mOwnScrollY;
6516 }
Aaron Heuckrothcd944dc2018-10-01 16:31:08 -04006517 mExpandedInThisMotion = true;
6518 }
6519 }
6520
6521 @Override
6522 public int getMaxExpandHeight(ExpandableView view) {
6523 return view.getMaxContentHeight();
6524 }
6525 };
6526
6527 public ExpandHelper.Callback getExpandHelperCallback() {
6528 return mExpandHelperCallback;
Aaron Heuckroth45d20be2018-09-18 13:47:26 -04006529 }
Ned Burns61269442019-05-02 18:27:23 -04006530
6531 /** Enum for selecting some or all notification rows (does not included non-notif views). */
6532 @Retention(SOURCE)
6533 @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
6534 public @interface SelectedRows {}
6535 /** All rows representing notifs. */
6536 public static final int ROWS_ALL = 0;
6537 /** Only rows where entry.isHighPriority() is true. */
6538 public static final int ROWS_HIGH_PRIORITY = 1;
6539 /** Only rows where entry.isHighPriority() is false. */
6540 public static final int ROWS_GENTLE = 2;
Selim Cinek67b22602014-03-10 15:40:16 +01006541}