blob: 602b9141d8eadeb08c96dd884e88ebef4905c339 [file] [log] [blame]
Daniel Sandler08d05e32012-08-08 16:39:54 -04001/*
2 * Copyright (C) 2012 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
17package com.android.systemui.statusbar.phone;
18
Jorim Jaggid7daab72014-05-06 22:22:20 +020019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Jorim Jaggi75c95042014-05-16 19:09:59 +020021import android.animation.ObjectAnimator;
Jorim Jaggid7daab72014-05-06 22:22:20 +020022import android.animation.ValueAnimator;
Daniel Sandler08d05e32012-08-08 16:39:54 -040023import android.content.Context;
Selim Cinek4c6969a2014-05-26 19:22:17 +020024import android.content.res.Configuration;
Daniel Sandler08d05e32012-08-08 16:39:54 -040025import android.util.AttributeSet;
Daniel Sandler040c2e42012-10-17 00:56:33 -040026import android.view.MotionEvent;
Jorim Jaggid7daab72014-05-06 22:22:20 +020027import android.view.VelocityTracker;
Daniel Sandler13522a22012-09-27 14:46:58 -040028import android.view.View;
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +020029import android.view.ViewGroup;
Jorim Jaggi75c95042014-05-16 19:09:59 +020030import android.view.ViewTreeObserver;
Casey Burkhardt23b0a4e2013-04-29 12:18:32 -070031import android.view.accessibility.AccessibilityEvent;
Jorim Jaggi75c95042014-05-16 19:09:59 +020032import android.view.animation.AnimationUtils;
33import android.view.animation.Interpolator;
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +020034import android.widget.LinearLayout;
Selim Cinekf99d0002014-06-13 07:36:01 +020035import android.widget.TextView;
Daniel Sandler13522a22012-09-27 14:46:58 -040036
Chet Haase4d179dc2012-08-22 07:14:42 -070037import com.android.systemui.R;
Jorim Jaggie65e3102014-07-01 22:00:50 +020038import com.android.systemui.qs.QSPanel;
Jorim Jaggibe565df2014-04-28 17:51:23 +020039import com.android.systemui.statusbar.ExpandableView;
Jorim Jaggi87cd5e72014-05-12 23:29:10 +020040import com.android.systemui.statusbar.FlingAnimationUtils;
Daniel Sandler151f00d2012-10-02 22:33:08 -040041import com.android.systemui.statusbar.GestureRecorder;
Selim Cinekbaa23272014-07-08 18:01:07 +020042import com.android.systemui.statusbar.KeyguardAffordanceView;
Jorim Jaggi605f1902014-06-25 05:23:42 +020043import com.android.systemui.statusbar.MirrorView;
Jorim Jaggiecbab362014-04-23 16:13:15 +020044import com.android.systemui.statusbar.StatusBarState;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010045import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Jorim Jaggi75c95042014-05-16 19:09:59 +020046import com.android.systemui.statusbar.stack.StackStateAnimator;
Daniel Sandler08d05e32012-08-08 16:39:54 -040047
Jorim Jaggibe565df2014-04-28 17:51:23 +020048public class NotificationPanelView extends PanelView implements
Jorim Jaggid7daab72014-05-06 22:22:20 +020049 ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
Jorim Jaggi290600a2014-05-30 17:02:20 +020050 View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
Selim Cinekbaa23272014-07-08 18:01:07 +020051 KeyguardAffordanceHelper.Callback {
Chet Haase4d179dc2012-08-22 07:14:42 -070052
Selim Cinekf99d0002014-06-13 07:36:01 +020053 // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
54 // changed.
55 private static final int CAP_HEIGHT = 1456;
56 private static final int FONT_HEIGHT = 2163;
57
Jorim Jaggi2580a9762014-06-25 03:08:25 +020058 private static final float HEADER_RUBBERBAND_FACTOR = 2.15f;
Jorim Jaggi8dd95e02014-06-03 16:19:33 +020059 private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
Selim Cineka02fe522014-05-30 17:01:21 +020060
Selim Cinekbaa23272014-07-08 18:01:07 +020061 private KeyguardAffordanceHelper mAfforanceHelper;
Jorim Jaggid7daab72014-05-06 22:22:20 +020062 private StatusBarHeaderView mHeader;
Jorim Jaggi4538027d2014-07-30 15:43:13 +020063 private KeyguardStatusBarView mKeyguardStatusBar;
John Spurlockaf8d6c42014-05-07 17:49:08 -040064 private View mQsContainer;
Jorim Jaggie65e3102014-07-01 22:00:50 +020065 private QSPanel mQsPanel;
John Spurlock73203eb2014-04-15 16:14:46 -040066 private View mKeyguardStatusView;
Jorim Jaggid7daab72014-05-06 22:22:20 +020067 private ObservableScrollView mScrollView;
Selim Cinekf99d0002014-06-13 07:36:01 +020068 private TextView mClockView;
Jorim Jaggi54045422014-07-03 18:30:40 +020069 private View mReserveNotificationSpace;
Jorim Jaggi605f1902014-06-25 05:23:42 +020070 private MirrorView mSystemIconsCopy;
71
Selim Cinekb6d85eb2014-03-28 20:21:01 +010072 private NotificationStackScrollLayout mNotificationStackScroller;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +020073 private int mNotificationTopPadding;
Jorim Jaggi98fb09c2014-05-01 22:40:56 +020074 private boolean mAnimateNextTopPaddingChange;
Chet Haase4d179dc2012-08-22 07:14:42 -070075
Jorim Jaggid7daab72014-05-06 22:22:20 +020076 private int mTrackingPointer;
77 private VelocityTracker mVelocityTracker;
Selim Cinek4c6969a2014-05-26 19:22:17 +020078 private boolean mQsTracking;
Jorim Jaggifc1a8242014-05-12 19:30:05 +020079
80 /**
Jorim Jaggidc96d632014-07-01 18:48:52 +020081 * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
82 * the expansion for quick settings.
83 */
84 private boolean mConflictingQsExpansionGesture;
85
86 /**
Jorim Jaggifc1a8242014-05-12 19:30:05 +020087 * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
88 * intercepted yet.
89 */
90 private boolean mIntercepting;
Jorim Jaggid7daab72014-05-06 22:22:20 +020091 private boolean mQsExpanded;
Jorim Jaggi30c305c2014-07-01 23:34:41 +020092 private boolean mQsExpandedWhenExpandingStarted;
John Spurlockbc3a5e92014-05-30 16:20:05 -040093 private boolean mQsFullyExpanded;
Jorim Jaggif62f2bb2014-05-25 22:28:41 +020094 private boolean mKeyguardShowing;
Jorim Jaggi56306252014-07-03 00:40:09 +020095 private int mStatusBarState;
Jorim Jaggid7daab72014-05-06 22:22:20 +020096 private float mInitialHeightOnTouch;
97 private float mInitialTouchX;
98 private float mInitialTouchY;
Jorim Jaggifc1a8242014-05-12 19:30:05 +020099 private float mLastTouchX;
100 private float mLastTouchY;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200101 private float mQsExpansionHeight;
102 private int mQsMinExpansionHeight;
103 private int mQsMaxExpansionHeight;
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200104 private int mQsPeekHeight;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200105 private boolean mStackScrollerOverscrolling;
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200106 private boolean mQsExpansionFromOverscroll;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200107 private boolean mQsExpansionEnabled = true;
108 private ValueAnimator mQsExpansionAnimator;
Jorim Jaggi87cd5e72014-05-12 23:29:10 +0200109 private FlingAnimationUtils mFlingAnimationUtils;
Selim Cinek9dbbca82014-05-15 15:23:37 +0200110 private int mStatusBarMinHeight;
Jorim Jaggi8dd95e02014-06-03 16:19:33 +0200111 private boolean mUnlockIconActive;
Jorim Jaggi457cc352014-06-02 22:47:42 +0200112 private int mNotificationsHeaderCollideDistance;
Jorim Jaggi8dd95e02014-06-03 16:19:33 +0200113 private int mUnlockMoveDistance;
Jorim Jaggi48bc36a2014-07-25 23:16:04 +0200114 private float mEmptyDragAmount;
John Spurlock3e04cc82014-05-30 12:34:03 -0400115
Jorim Jaggi75c95042014-05-16 19:09:59 +0200116 private Interpolator mFastOutSlowInInterpolator;
Jorim Jaggi457cc352014-06-02 22:47:42 +0200117 private Interpolator mFastOutLinearInterpolator;
Jorim Jaggi75c95042014-05-16 19:09:59 +0200118 private ObjectAnimator mClockAnimator;
119 private int mClockAnimationTarget = -1;
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200120 private int mTopPaddingAdjustment;
121 private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
122 new KeyguardClockPositionAlgorithm();
123 private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
124 new KeyguardClockPositionAlgorithm.Result();
Selim Cinek4c6969a2014-05-26 19:22:17 +0200125 private boolean mIsExpanding;
Selim Cinekf99d0002014-06-13 07:36:01 +0200126
Selim Cinek4c6969a2014-05-26 19:22:17 +0200127 private boolean mBlockTouches;
Selim Cinekf54090e2014-06-17 17:24:51 -0700128 private int mNotificationScrimWaitDistance;
Jorim Jaggi1e2554b2014-07-10 23:07:00 +0200129 private boolean mTwoFingerQsExpand;
130 private boolean mTwoFingerQsExpandPossible;
Jorim Jaggi56306252014-07-03 00:40:09 +0200131
132 /**
133 * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
134 * need to take this into account in our panel height calculation.
135 */
136 private int mScrollYOverride = -1;
Jorim Jaggibd003102014-07-04 00:02:42 +0200137 private boolean mQsAnimatorExpand;
Selim Cinekbaa23272014-07-08 18:01:07 +0200138 private boolean mIsLaunchTransitionFinished;
139 private boolean mIsLaunchTransitionRunning;
140 private Runnable mLaunchAnimationEndRunnable;
141 private boolean mOnlyAffordanceInThisMotion;
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200142 private boolean mKeyguardStatusBarAnimatingIn;
Jorim Jaggi069cd032014-05-15 03:09:01 +0200143
Daniel Sandler08d05e32012-08-08 16:39:54 -0400144 public NotificationPanelView(Context context, AttributeSet attrs) {
145 super(context, attrs);
Jorim Jaggi605f1902014-06-25 05:23:42 +0200146 mSystemIconsCopy = new MirrorView(context);
Daniel Sandler13522a22012-09-27 14:46:58 -0400147 }
Chet Haase4d179dc2012-08-22 07:14:42 -0700148
Daniel Sandler040c2e42012-10-17 00:56:33 -0400149 public void setStatusBar(PhoneStatusBar bar) {
150 mStatusBar = bar;
151 }
152
Daniel Sandler13522a22012-09-27 14:46:58 -0400153 @Override
154 protected void onFinishInflate() {
155 super.onFinishInflate();
Jorim Jaggid7daab72014-05-06 22:22:20 +0200156 mHeader = (StatusBarHeaderView) findViewById(R.id.header);
Jorim Jaggi853b0702014-07-05 04:31:14 +0200157 mHeader.setOnClickListener(this);
Jorim Jaggi4538027d2014-07-30 15:43:13 +0200158 mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
John Spurlock73203eb2014-04-15 16:14:46 -0400159 mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400160 mQsContainer = findViewById(R.id.quick_settings_container);
Jorim Jaggie65e3102014-07-01 22:00:50 +0200161 mQsPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
Selim Cinekf99d0002014-06-13 07:36:01 +0200162 mClockView = (TextView) findViewById(R.id.clock_view);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200163 mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
164 mScrollView.setListener(this);
Jorim Jaggi54045422014-07-03 18:30:40 +0200165 mReserveNotificationSpace = findViewById(R.id.reserve_notification_space);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100166 mNotificationStackScroller = (NotificationStackScrollLayout)
167 findViewById(R.id.notification_stack_scroller);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200168 mNotificationStackScroller.setOnHeightChangedListener(this);
Jorim Jaggi290600a2014-05-30 17:02:20 +0200169 mNotificationStackScroller.setOverscrollTopChangedListener(this);
Jorim Jaggi56306252014-07-03 00:40:09 +0200170 mNotificationStackScroller.setScrollView(mScrollView);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200171 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
172 android.R.interpolator.fast_out_slow_in);
Jorim Jaggi457cc352014-06-02 22:47:42 +0200173 mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(),
174 android.R.interpolator.fast_out_linear_in);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200175 mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
Selim Cinekbaa23272014-07-08 18:01:07 +0200176 mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
Jorim Jaggi069cd032014-05-15 03:09:01 +0200177 }
178
179 @Override
180 protected void loadDimens() {
181 super.loadDimens();
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200182 mNotificationTopPadding = getResources().getDimensionPixelSize(
183 R.dimen.notifications_top_padding);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200184 mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
Selim Cinek9dbbca82014-05-15 15:23:37 +0200185 mStatusBarMinHeight = getResources().getDimensionPixelSize(
186 com.android.internal.R.dimen.status_bar_height);
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200187 mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height);
Jorim Jaggi457cc352014-06-02 22:47:42 +0200188 mNotificationsHeaderCollideDistance =
189 getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
Jorim Jaggi8dd95e02014-06-03 16:19:33 +0200190 mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200191 mClockPositionAlgorithm.loadDimens(getResources());
Selim Cinekf54090e2014-06-17 17:24:51 -0700192 mNotificationScrimWaitDistance =
193 getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200194 }
195
196 @Override
197 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
198 super.onLayout(changed, left, top, right, bottom);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200199
Selim Cinekf99d0002014-06-13 07:36:01 +0200200 // Update Clock Pivot
201 mKeyguardStatusView.setPivotX(getWidth() / 2);
Jorim Jaggi4538027d2014-07-30 15:43:13 +0200202 mKeyguardStatusView.setPivotY((FONT_HEIGHT - CAP_HEIGHT) / 2048f * mClockView.getTextSize());
Selim Cinekf99d0002014-06-13 07:36:01 +0200203
Jorim Jaggid7daab72014-05-06 22:22:20 +0200204 // Calculate quick setting heights.
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200205 mQsMinExpansionHeight = mKeyguardShowing ? 0 : mHeader.getCollapsedHeight() + mQsPeekHeight;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200206 mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
John Spurlock2684d5e2014-05-28 20:27:44 -0400207 if (mQsExpanded) {
John Spurlockbc3a5e92014-05-30 16:20:05 -0400208 if (mQsFullyExpanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +0200209 mQsExpansionHeight = mQsMaxExpansionHeight;
210 requestScrollerTopPaddingUpdate(false /* animate */);
John Spurlockbc3a5e92014-05-30 16:20:05 -0400211 }
John Spurlock2684d5e2014-05-28 20:27:44 -0400212 } else {
Jorim Jaggi290600a2014-05-30 17:02:20 +0200213 if (!mStackScrollerOverscrolling) {
214 setQsExpansion(mQsMinExpansionHeight);
215 }
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200216 positionClockAndNotifications();
217 mNotificationStackScroller.setStackHeight(getExpandedHeight());
Jorim Jaggid7daab72014-05-06 22:22:20 +0200218 }
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200219 }
220
Jorim Jaggi069cd032014-05-15 03:09:01 +0200221 /**
222 * Positions the clock and notifications dynamically depending on how many notifications are
223 * showing.
224 */
225 private void positionClockAndNotifications() {
Selim Cinekf99d0002014-06-13 07:36:01 +0200226 boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
Selim Cinek1408eb52014-06-02 14:45:38 +0200227 int stackScrollerPadding;
Jorim Jaggi4538027d2014-07-30 15:43:13 +0200228 if (mStatusBarState != StatusBarState.KEYGUARD) {
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200229 int bottom = mHeader.getCollapsedHeight();
Jorim Jaggi4538027d2014-07-30 15:43:13 +0200230 stackScrollerPadding = mStatusBarState == StatusBarState.SHADE
231 ? bottom + mQsPeekHeight + mNotificationTopPadding
232 : mKeyguardStatusBar.getHeight() + mNotificationTopPadding;
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200233 mTopPaddingAdjustment = 0;
Jorim Jaggi069cd032014-05-15 03:09:01 +0200234 } else {
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200235 mClockPositionAlgorithm.setup(
236 mStatusBar.getMaxKeyguardNotifications(),
237 getMaxPanelHeight(),
238 getExpandedHeight(),
239 mNotificationStackScroller.getNotGoneChildCount(),
240 getHeight(),
Jorim Jaggi48bc36a2014-07-25 23:16:04 +0200241 mKeyguardStatusView.getHeight(),
242 mEmptyDragAmount);
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200243 mClockPositionAlgorithm.run(mClockPositionResult);
Selim Cinekf99d0002014-06-13 07:36:01 +0200244 if (animate || mClockAnimator != null) {
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200245 startClockAnimation(mClockPositionResult.clockY);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200246 } else {
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200247 mKeyguardStatusView.setY(mClockPositionResult.clockY);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200248 }
Selim Cinekf99d0002014-06-13 07:36:01 +0200249 updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale);
Selim Cinek1408eb52014-06-02 14:45:38 +0200250 stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200251 mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
Jorim Jaggi069cd032014-05-15 03:09:01 +0200252 }
Selim Cinek1408eb52014-06-02 14:45:38 +0200253 mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
Selim Cinekf99d0002014-06-13 07:36:01 +0200254 requestScrollerTopPaddingUpdate(animate);
Jorim Jaggi069cd032014-05-15 03:09:01 +0200255 }
256
Jorim Jaggi75c95042014-05-16 19:09:59 +0200257 private void startClockAnimation(int y) {
258 if (mClockAnimationTarget == y) {
259 return;
260 }
261 mClockAnimationTarget = y;
262 getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
263 @Override
264 public boolean onPreDraw() {
265 getViewTreeObserver().removeOnPreDrawListener(this);
266 if (mClockAnimator != null) {
267 mClockAnimator.removeAllListeners();
268 mClockAnimator.cancel();
269 }
Jorim Jaggia1b1c7a2014-06-03 16:29:05 +0200270 mClockAnimator = ObjectAnimator
271 .ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget);
Jorim Jaggi75c95042014-05-16 19:09:59 +0200272 mClockAnimator.setInterpolator(mFastOutSlowInInterpolator);
273 mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
274 mClockAnimator.addListener(new AnimatorListenerAdapter() {
275 @Override
276 public void onAnimationEnd(Animator animation) {
277 mClockAnimator = null;
278 mClockAnimationTarget = -1;
279 }
280 });
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200281 mClockAnimator.start();
Jorim Jaggi75c95042014-05-16 19:09:59 +0200282 return true;
283 }
284 });
285 }
286
Selim Cinekf99d0002014-06-13 07:36:01 +0200287 private void updateClock(float alpha, float scale) {
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200288 mKeyguardStatusView.setAlpha(alpha);
Selim Cinekf99d0002014-06-13 07:36:01 +0200289 mKeyguardStatusView.setScaleX(scale);
290 mKeyguardStatusView.setScaleY(scale);
Jorim Jaggi069cd032014-05-15 03:09:01 +0200291 }
292
Selim Cinekc27437b2014-05-14 10:23:33 +0200293 public void animateToFullShade() {
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200294 mAnimateNextTopPaddingChange = true;
Selim Cinekc27437b2014-05-14 10:23:33 +0200295 mNotificationStackScroller.goToFullShade();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200296 requestLayout();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400297 }
298
Jorim Jaggid7daab72014-05-06 22:22:20 +0200299 public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
300 mQsExpansionEnabled = qsExpansionEnabled;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200301 }
302
Selim Cinek3c4635c2014-05-29 02:12:47 +0200303 @Override
Selim Cinek4c6969a2014-05-26 19:22:17 +0200304 public void resetViews() {
Selim Cinekbaa23272014-07-08 18:01:07 +0200305 mIsLaunchTransitionFinished = false;
Selim Cinek4c6969a2014-05-26 19:22:17 +0200306 mBlockTouches = false;
Jorim Jaggi8dd95e02014-06-03 16:19:33 +0200307 mUnlockIconActive = false;
Selim Cinekbaa23272014-07-08 18:01:07 +0200308 mAfforanceHelper.reset(true);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200309 closeQs();
Jorim Jaggidbbf45e2014-06-25 21:54:48 +0200310 mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
311 true /* cancelAnimators */);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200312 }
313
Jorim Jaggid7daab72014-05-06 22:22:20 +0200314 public void closeQs() {
315 cancelAnimation();
316 setQsExpansion(mQsMinExpansionHeight);
317 }
318
Jorim Jaggibd003102014-07-04 00:02:42 +0200319 public void animateCloseQs() {
320 if (mQsExpansionAnimator != null) {
321 if (!mQsAnimatorExpand) {
322 return;
323 }
324 float height = mQsExpansionHeight;
325 mQsExpansionAnimator.cancel();
326 setQsExpansion(height);
327 }
328 flingSettings(0 /* vel */, false);
329 }
330
Jorim Jaggid7daab72014-05-06 22:22:20 +0200331 public void openQs() {
332 cancelAnimation();
333 if (mQsExpansionEnabled) {
334 setQsExpansion(mQsMaxExpansionHeight);
335 }
336 }
337
Daniel Sandler08d05e32012-08-08 16:39:54 -0400338 @Override
339 public void fling(float vel, boolean always) {
Daniel Sandler151f00d2012-10-02 22:33:08 -0400340 GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
341 if (gr != null) {
Selim Cinek4c6969a2014-05-26 19:22:17 +0200342 gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
Daniel Sandler151f00d2012-10-02 22:33:08 -0400343 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400344 super.fling(vel, always);
345 }
Chet Haase4d179dc2012-08-22 07:14:42 -0700346
Casey Burkhardt23b0a4e2013-04-29 12:18:32 -0700347 @Override
348 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
349 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
350 event.getText()
351 .add(getContext().getString(R.string.accessibility_desc_notification_shade));
352 return true;
353 }
354
355 return super.dispatchPopulateAccessibilityEvent(event);
356 }
357
Daniel Sandler040c2e42012-10-17 00:56:33 -0400358 @Override
John Spurlock73203eb2014-04-15 16:14:46 -0400359 public boolean onInterceptTouchEvent(MotionEvent event) {
Selim Cinek4c6969a2014-05-26 19:22:17 +0200360 if (mBlockTouches) {
361 return false;
362 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200363 resetDownStates(event);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200364 int pointerIndex = event.findPointerIndex(mTrackingPointer);
365 if (pointerIndex < 0) {
366 pointerIndex = 0;
367 mTrackingPointer = event.getPointerId(pointerIndex);
John Spurlock73203eb2014-04-15 16:14:46 -0400368 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200369 final float x = event.getX(pointerIndex);
370 final float y = event.getY(pointerIndex);
371
372 switch (event.getActionMasked()) {
373 case MotionEvent.ACTION_DOWN:
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200374 mIntercepting = true;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200375 mInitialTouchY = y;
376 mInitialTouchX = x;
377 initVelocityTracker();
378 trackMovement(event);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200379 if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200380 getParent().requestDisallowInterceptTouchEvent(true);
381 }
Jorim Jaggi54045422014-07-03 18:30:40 +0200382 if (mQsExpansionAnimator != null) {
383 onQsExpansionStarted();
384 mInitialHeightOnTouch = mQsExpansionHeight;
385 mQsTracking = true;
386 mIntercepting = false;
387 mNotificationStackScroller.removeLongPressCallback();
388 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200389 break;
390 case MotionEvent.ACTION_POINTER_UP:
391 final int upPointer = event.getPointerId(event.getActionIndex());
392 if (mTrackingPointer == upPointer) {
393 // gesture is ongoing, find a new pointer to track
394 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
395 mTrackingPointer = event.getPointerId(newIndex);
396 mInitialTouchX = event.getX(newIndex);
397 mInitialTouchY = event.getY(newIndex);
398 }
399 break;
400
401 case MotionEvent.ACTION_MOVE:
402 final float h = y - mInitialTouchY;
403 trackMovement(event);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200404 if (mQsTracking) {
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200405
406 // Already tracking because onOverscrolled was called. We need to update here
407 // so we don't stop for a frame until the next touch event gets handled in
408 // onTouchEvent.
409 setQsExpansion(h + mInitialHeightOnTouch);
410 trackMovement(event);
411 mIntercepting = false;
412 return true;
413 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200414 if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
Selim Cinek4c6969a2014-05-26 19:22:17 +0200415 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200416 onQsExpansionStarted();
417 mInitialHeightOnTouch = mQsExpansionHeight;
418 mInitialTouchY = y;
419 mInitialTouchX = x;
Selim Cinek4c6969a2014-05-26 19:22:17 +0200420 mQsTracking = true;
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200421 mIntercepting = false;
Selim Cinek1408eb52014-06-02 14:45:38 +0200422 mNotificationStackScroller.removeLongPressCallback();
Jorim Jaggid7daab72014-05-06 22:22:20 +0200423 return true;
424 }
425 break;
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200426
427 case MotionEvent.ACTION_CANCEL:
428 case MotionEvent.ACTION_UP:
Jorim Jaggic9a52ab2014-05-12 23:46:19 +0200429 trackMovement(event);
Selim Cinek4c6969a2014-05-26 19:22:17 +0200430 if (mQsTracking) {
431 flingQsWithCurrentVelocity();
432 mQsTracking = false;
Jorim Jaggic9a52ab2014-05-12 23:46:19 +0200433 }
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200434 mIntercepting = false;
435 break;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200436 }
437 return !mQsExpanded && super.onInterceptTouchEvent(event);
John Spurlock73203eb2014-04-15 16:14:46 -0400438 }
439
Selim Cinekbaa23272014-07-08 18:01:07 +0200440 private void resetDownStates(MotionEvent event) {
441 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
442 mOnlyAffordanceInThisMotion = false;
443 }
444 }
445
John Spurlock73203eb2014-04-15 16:14:46 -0400446 @Override
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200447 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
448
Jorim Jaggice50b812014-05-24 16:29:57 +0200449 // Block request when interacting with the scroll view so we can still intercept the
450 // scrolling when QS is expanded.
Jorim Jaggi3f48f462014-07-08 16:53:29 +0200451 if (mScrollView.isHandlingTouchEvent()) {
Jorim Jaggice50b812014-05-24 16:29:57 +0200452 return;
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200453 }
Jorim Jaggice50b812014-05-24 16:29:57 +0200454 super.requestDisallowInterceptTouchEvent(disallowIntercept);
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200455 }
456
Selim Cinek4c6969a2014-05-26 19:22:17 +0200457 private void flingQsWithCurrentVelocity() {
Jorim Jaggic9a52ab2014-05-12 23:46:19 +0200458 float vel = getCurrentVelocity();
Jorim Jaggidc96d632014-07-01 18:48:52 +0200459 flingSettings(vel, flingExpandsQs(vel));
Jorim Jaggic9a52ab2014-05-12 23:46:19 +0200460 }
461
Jorim Jaggidc96d632014-07-01 18:48:52 +0200462 private boolean flingExpandsQs(float vel) {
463 if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
464 return getQsExpansionFraction() > 0.5f;
465 } else {
466 return vel > 0;
467 }
468 }
469
470 private float getQsExpansionFraction() {
471 return (mQsExpansionHeight - mQsMinExpansionHeight)
Jorim Jaggi54045422014-07-03 18:30:40 +0200472 / (getTempQsMaxExpansion() - mQsMinExpansionHeight);
Jorim Jaggidc96d632014-07-01 18:48:52 +0200473 }
Jorim Jaggi54045422014-07-03 18:30:40 +0200474
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200475 @Override
Daniel Sandler040c2e42012-10-17 00:56:33 -0400476 public boolean onTouchEvent(MotionEvent event) {
Selim Cinek4c6969a2014-05-26 19:22:17 +0200477 if (mBlockTouches) {
478 return false;
479 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200480 resetDownStates(event);
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +0200481 if ((!mIsExpanding || mHintAnimationRunning)
482 && !mQsExpanded
483 && mStatusBar.getBarState() != StatusBarState.SHADE) {
Selim Cinekbaa23272014-07-08 18:01:07 +0200484 mAfforanceHelper.onTouchEvent(event);
485 }
486 if (mOnlyAffordanceInThisMotion) {
487 return true;
Selim Cinek4c6969a2014-05-26 19:22:17 +0200488 }
Jorim Jaggidc96d632014-07-01 18:48:52 +0200489 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
490 && mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200491
Jorim Jaggidc96d632014-07-01 18:48:52 +0200492 // Down in the empty area while fully expanded - go to QS.
493 mQsTracking = true;
494 mConflictingQsExpansionGesture = true;
495 onQsExpansionStarted();
496 mInitialHeightOnTouch = mQsExpansionHeight;
497 mInitialTouchY = event.getX();
498 mInitialTouchX = event.getY();
499 }
Jorim Jaggi853b0702014-07-05 04:31:14 +0200500 if (mExpandedHeight != 0) {
501 handleQsDown(event);
502 }
Jorim Jaggi1e2554b2014-07-10 23:07:00 +0200503 if (!mTwoFingerQsExpand && (mQsTracking || mQsExpanded)) {
Jorim Jaggidc96d632014-07-01 18:48:52 +0200504 onQsTouch(event);
505 if (!mConflictingQsExpansionGesture) {
506 return true;
507 }
508 }
509 if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
510 || event.getActionMasked() == MotionEvent.ACTION_UP) {
511 mConflictingQsExpansionGesture = false;
512 }
Jorim Jaggi1e2554b2014-07-10 23:07:00 +0200513 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mExpandedHeight == 0) {
514 mTwoFingerQsExpandPossible = true;
515 }
516 if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
517 && event.getPointerCount() == 2
518 && event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
519 mTwoFingerQsExpand = true;
520 requestPanelHeightUpdate();
521 }
Selim Cinek4c6969a2014-05-26 19:22:17 +0200522 super.onTouchEvent(event);
523 return true;
524 }
525
Jorim Jaggi853b0702014-07-05 04:31:14 +0200526 private void handleQsDown(MotionEvent event) {
527 if (event.getActionMasked() == MotionEvent.ACTION_DOWN
528 && shouldQuickSettingsIntercept(event.getX(), event.getY(), 0)) {
529 mQsTracking = true;
530 onQsExpansionStarted();
531 mInitialHeightOnTouch = mQsExpansionHeight;
532 mInitialTouchY = event.getX();
533 mInitialTouchX = event.getY();
534 }
535 }
536
Selim Cinek4c6969a2014-05-26 19:22:17 +0200537 @Override
Jorim Jaggidc96d632014-07-01 18:48:52 +0200538 protected boolean flingExpands(float vel, float vectorVel) {
539 boolean expands = super.flingExpands(vel, vectorVel);
540
541 // If we are already running a QS expansion, make sure that we keep the panel open.
542 if (mQsExpansionAnimator != null) {
543 expands = true;
544 }
545 return expands;
546 }
547
548 @Override
Selim Cinek4c6969a2014-05-26 19:22:17 +0200549 protected boolean hasConflictingGestures() {
550 return mStatusBar.getBarState() != StatusBarState.SHADE;
551 }
552
Jorim Jaggidc96d632014-07-01 18:48:52 +0200553 private void onQsTouch(MotionEvent event) {
Selim Cinek4c6969a2014-05-26 19:22:17 +0200554 int pointerIndex = event.findPointerIndex(mTrackingPointer);
555 if (pointerIndex < 0) {
556 pointerIndex = 0;
557 mTrackingPointer = event.getPointerId(pointerIndex);
558 }
559 final float y = event.getY(pointerIndex);
560 final float x = event.getX(pointerIndex);
561
562 switch (event.getActionMasked()) {
563 case MotionEvent.ACTION_DOWN:
564 mQsTracking = true;
565 mInitialTouchY = y;
566 mInitialTouchX = x;
567 onQsExpansionStarted();
568 mInitialHeightOnTouch = mQsExpansionHeight;
569 initVelocityTracker();
570 trackMovement(event);
571 break;
572
573 case MotionEvent.ACTION_POINTER_UP:
574 final int upPointer = event.getPointerId(event.getActionIndex());
575 if (mTrackingPointer == upPointer) {
576 // gesture is ongoing, find a new pointer to track
577 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
578 final float newY = event.getY(newIndex);
579 final float newX = event.getX(newIndex);
580 mTrackingPointer = event.getPointerId(newIndex);
581 mInitialHeightOnTouch = mQsExpansionHeight;
582 mInitialTouchY = newY;
583 mInitialTouchX = newX;
584 }
585 break;
586
587 case MotionEvent.ACTION_MOVE:
588 final float h = y - mInitialTouchY;
589 setQsExpansion(h + mInitialHeightOnTouch);
590 trackMovement(event);
591 break;
592
593 case MotionEvent.ACTION_UP:
594 case MotionEvent.ACTION_CANCEL:
595 mQsTracking = false;
596 mTrackingPointer = -1;
597 trackMovement(event);
Jorim Jaggidc96d632014-07-01 18:48:52 +0200598 float fraction = getQsExpansionFraction();
599 if ((fraction != 0f || y >= mInitialTouchY)
600 && (fraction != 1f || y <= mInitialTouchY)) {
601 flingQsWithCurrentVelocity();
Jorim Jaggi54045422014-07-03 18:30:40 +0200602 } else {
603 mScrollYOverride = -1;
Jorim Jaggidc96d632014-07-01 18:48:52 +0200604 }
Selim Cinek4c6969a2014-05-26 19:22:17 +0200605 if (mVelocityTracker != null) {
606 mVelocityTracker.recycle();
607 mVelocityTracker = null;
608 }
609 break;
610 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200611 }
612
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200613 @Override
614 public void onOverscrolled(int amount) {
615 if (mIntercepting) {
616 onQsExpansionStarted(amount);
617 mInitialHeightOnTouch = mQsExpansionHeight;
618 mInitialTouchY = mLastTouchY;
619 mInitialTouchX = mLastTouchX;
Selim Cinek4c6969a2014-05-26 19:22:17 +0200620 mQsTracking = true;
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200621 }
622 }
623
Jorim Jaggi290600a2014-05-30 17:02:20 +0200624 @Override
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200625 public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
Jorim Jaggi290600a2014-05-30 17:02:20 +0200626 cancelAnimation();
627 float rounded = amount >= 1f ? amount : 0f;
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200628 mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
629 mQsExpansionFromOverscroll = rounded != 0f;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200630 updateQsState();
Jorim Jaggi90e0df22014-07-30 01:10:35 +0200631 setQsExpansion(mQsMinExpansionHeight + rounded);
Jorim Jaggi290600a2014-05-30 17:02:20 +0200632 }
633
Selim Cinek1408eb52014-06-02 14:45:38 +0200634 @Override
635 public void flingTopOverscroll(float velocity, boolean open) {
Selim Cinek1408eb52014-06-02 14:45:38 +0200636 setQsExpansion(mQsExpansionHeight);
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200637 flingSettings(velocity, open, new Runnable() {
638 @Override
639 public void run() {
640 mStackScrollerOverscrolling = false;
641 mQsExpansionFromOverscroll = false;
Jorim Jaggi56306252014-07-03 00:40:09 +0200642 updateQsState();
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200643 }
644 });
Selim Cinek1408eb52014-06-02 14:45:38 +0200645 }
646
Jorim Jaggid7daab72014-05-06 22:22:20 +0200647 private void onQsExpansionStarted() {
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200648 onQsExpansionStarted(0);
649 }
650
651 private void onQsExpansionStarted(int overscrollAmount) {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200652 cancelAnimation();
653
654 // Reset scroll position and apply that position to the expanded height.
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200655 float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount;
Jorim Jaggi54045422014-07-03 18:30:40 +0200656 if (mScrollView.getScrollY() != 0) {
657 mScrollYOverride = mScrollView.getScrollY();
658 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200659 mScrollView.scrollTo(0, 0);
660 setQsExpansion(height);
661 }
662
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200663 private void setQsExpanded(boolean expanded) {
664 boolean changed = mQsExpanded != expanded;
665 if (changed) {
666 mQsExpanded = expanded;
667 updateQsState();
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200668 requestPanelHeightUpdate();
Jorim Jaggi56306252014-07-03 00:40:09 +0200669 mNotificationStackScroller.setInterceptDelegateEnabled(expanded);
Jorim Jaggib690f0d2014-07-03 23:25:44 +0200670 mStatusBar.setQsExpanded(expanded);
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200671 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200672 }
673
Jorim Jaggi56306252014-07-03 00:40:09 +0200674 public void setBarState(int statusBarState) {
675 boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD
676 || statusBarState == StatusBarState.SHADE_LOCKED;
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200677 mKeyguardStatusBar.setAlpha(1f);
Jorim Jaggi4538027d2014-07-30 15:43:13 +0200678 mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200679 if (!mKeyguardShowing && keyguardShowing) {
680 setQsTranslation(mQsExpansionHeight);
681 mHeader.setTranslationY(0f);
682 }
Jorim Jaggi56306252014-07-03 00:40:09 +0200683 mStatusBarState = statusBarState;
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200684 mKeyguardShowing = keyguardShowing;
685 updateQsState();
686 }
687
688 private void updateQsState() {
Jorim Jaggi290600a2014-05-30 17:02:20 +0200689 boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
Jorim Jaggi4538027d2014-07-30 15:43:13 +0200690 mHeader.setVisibility((mQsExpanded || !mKeyguardShowing) ? View.VISIBLE : View.INVISIBLE);
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200691 mHeader.setExpanded(mKeyguardShowing || (mQsExpanded && !mStackScrollerOverscrolling));
Jorim Jaggi4538027d2014-07-30 15:43:13 +0200692 mNotificationStackScroller.setScrollingEnabled(
693 mStatusBarState != StatusBarState.KEYGUARD && (!mQsExpanded
694 || mQsExpansionFromOverscroll));
Jorim Jaggi290600a2014-05-30 17:02:20 +0200695 mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200696 mQsContainer.setVisibility(
697 mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200698 mScrollView.setTouchEnabled(mQsExpanded);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200699 }
700
701 private void setQsExpansion(float height) {
702 height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
John Spurlockbc3a5e92014-05-30 16:20:05 -0400703 mQsFullyExpanded = height == mQsMaxExpansionHeight;
Jorim Jaggi290600a2014-05-30 17:02:20 +0200704 if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling) {
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200705 setQsExpanded(true);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200706 } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
Jorim Jaggif62f2bb2014-05-25 22:28:41 +0200707 setQsExpanded(false);
John Spurlock73203eb2014-04-15 16:14:46 -0400708 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200709 mQsExpansionHeight = height;
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200710 mHeader.setExpansion(getHeaderExpansionFraction());
Jorim Jaggid7daab72014-05-06 22:22:20 +0200711 setQsTranslation(height);
Selim Cinek1408eb52014-06-02 14:45:38 +0200712 requestScrollerTopPaddingUpdate(false /* animate */);
Selim Cinekf54090e2014-06-17 17:24:51 -0700713 updateNotificationScrim(height);
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200714 if (mKeyguardShowing) {
715 float alpha = getQsExpansionFraction();
716 alpha *= 2;
717 alpha = Math.min(1, alpha);
718 alpha = 1 - alpha;
719 if (alpha == 0f) {
720 mKeyguardStatusBar.setVisibility(View.INVISIBLE);
721 } else {
722 mKeyguardStatusBar.setVisibility(View.VISIBLE);
723 mKeyguardStatusBar.setAlpha(alpha);
724 }
725 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200726 }
727
Selim Cinekf54090e2014-06-17 17:24:51 -0700728 private void updateNotificationScrim(float height) {
729 int startDistance = mQsMinExpansionHeight + mNotificationScrimWaitDistance;
730 float progress = (height - startDistance) / (mQsMaxExpansionHeight - startDistance);
731 progress = Math.max(0.0f, Math.min(progress, 1.0f));
732 mNotificationStackScroller.setScrimAlpha(progress);
733 }
734
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200735 private float getHeaderExpansionFraction() {
736 if (!mKeyguardShowing) {
737 return getQsExpansionFraction();
738 } else {
739 return 1f;
740 }
741 }
742
Jorim Jaggid7daab72014-05-06 22:22:20 +0200743 private void setQsTranslation(float height) {
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200744 mQsContainer.setY(height - mQsContainer.getHeight() + getHeaderTranslation());
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200745 if (mKeyguardShowing) {
746 mHeader.setY(interpolate(getQsExpansionFraction(), -mHeader.getHeight(), 0));
747 }
748 }
749
750 private float calculateQsTopPadding() {
751 if (mKeyguardShowing) {
752 return interpolate(getQsExpansionFraction(),
753 mNotificationStackScroller.getIntrinsicPadding() - mNotificationTopPadding,
754 mQsMaxExpansionHeight);
755 } else {
756 return mQsExpansionHeight;
757 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200758 }
759
Selim Cinek1408eb52014-06-02 14:45:38 +0200760 private void requestScrollerTopPaddingUpdate(boolean animate) {
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200761 mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(),
Selim Cinek1408eb52014-06-02 14:45:38 +0200762 mScrollView.getScrollY(),
763 mAnimateNextTopPaddingChange || animate);
Selim Cinek758afb12014-06-04 18:46:12 +0200764 mAnimateNextTopPaddingChange = false;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200765 }
766
767 private void trackMovement(MotionEvent event) {
768 if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200769 mLastTouchX = event.getX();
770 mLastTouchY = event.getY();
Jorim Jaggid7daab72014-05-06 22:22:20 +0200771 }
772
773 private void initVelocityTracker() {
774 if (mVelocityTracker != null) {
775 mVelocityTracker.recycle();
776 }
777 mVelocityTracker = VelocityTracker.obtain();
778 }
779
780 private float getCurrentVelocity() {
781 if (mVelocityTracker == null) {
782 return 0;
783 }
784 mVelocityTracker.computeCurrentVelocity(1000);
785 return mVelocityTracker.getYVelocity();
786 }
787
788 private void cancelAnimation() {
789 if (mQsExpansionAnimator != null) {
790 mQsExpansionAnimator.cancel();
791 }
792 }
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200793
Jorim Jaggid7daab72014-05-06 22:22:20 +0200794 private void flingSettings(float vel, boolean expand) {
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200795 flingSettings(vel, expand, null);
796 }
797
798 private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable) {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200799 float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
Jorim Jaggi9cd731a2014-05-22 14:26:06 +0200800 if (target == mQsExpansionHeight) {
Jorim Jaggi54045422014-07-03 18:30:40 +0200801 mScrollYOverride = -1;
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200802 if (onFinishRunnable != null) {
803 onFinishRunnable.run();
804 }
Jorim Jaggi9cd731a2014-05-22 14:26:06 +0200805 return;
806 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200807 ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
Jorim Jaggi87cd5e72014-05-12 23:29:10 +0200808 mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200809 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
810 @Override
811 public void onAnimationUpdate(ValueAnimator animation) {
812 setQsExpansion((Float) animation.getAnimatedValue());
813 }
814 });
815 animator.addListener(new AnimatorListenerAdapter() {
816 @Override
817 public void onAnimationEnd(Animator animation) {
Jorim Jaggi54045422014-07-03 18:30:40 +0200818 mScrollYOverride = -1;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200819 mQsExpansionAnimator = null;
Jorim Jaggi475b21d2014-07-01 18:13:24 +0200820 if (onFinishRunnable != null) {
821 onFinishRunnable.run();
822 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200823 }
824 });
825 animator.start();
826 mQsExpansionAnimator = animator;
Jorim Jaggibd003102014-07-04 00:02:42 +0200827 mQsAnimatorExpand = expand;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200828 }
829
830 /**
831 * @return Whether we should intercept a gesture to open Quick Settings.
832 */
Selim Cinek4c6969a2014-05-26 19:22:17 +0200833 private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200834 if (!mQsExpansionEnabled) {
835 return false;
836 }
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200837 View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader;
838 boolean onHeader = x >= header.getLeft() && x <= header.getRight()
839 && y >= header.getTop() && y <= header.getBottom();
Jorim Jaggid7daab72014-05-06 22:22:20 +0200840 if (mQsExpanded) {
John Spurlock3e04cc82014-05-30 12:34:03 -0400841 return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200842 } else {
843 return onHeader;
844 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100845 }
846
847 @Override
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +0200848 public void setVisibility(int visibility) {
849 int oldVisibility = getVisibility();
850 super.setVisibility(visibility);
851 if (visibility != oldVisibility) {
852 reparentStatusIcons(visibility == VISIBLE);
853 }
854 }
855
856 /**
857 * When the notification panel gets expanded, we need to move the status icons in the header
858 * card.
859 */
860 private void reparentStatusIcons(boolean toHeader) {
861 if (mStatusBar == null) {
862 return;
863 }
864 LinearLayout systemIcons = mStatusBar.getSystemIcons();
Jorim Jaggi605f1902014-06-25 05:23:42 +0200865 ViewGroup parent = ((ViewGroup) systemIcons.getParent());
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +0200866 if (toHeader) {
Jorim Jaggi605f1902014-06-25 05:23:42 +0200867 int index = parent.indexOfChild(systemIcons);
868 parent.removeView(systemIcons);
869 mSystemIconsCopy.setMirroredView(
870 systemIcons, systemIcons.getWidth(), systemIcons.getHeight());
871 parent.addView(mSystemIconsCopy, index);
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +0200872 mHeader.attachSystemIcons(systemIcons);
873 } else {
Jorim Jaggi605f1902014-06-25 05:23:42 +0200874 ViewGroup newParent = mStatusBar.getSystemIconArea();
875 int index = newParent.indexOfChild(mSystemIconsCopy);
876 parent.removeView(systemIcons);
Jorim Jaggi13c1b1f2014-05-11 21:55:00 +0200877 mHeader.onSystemIconsDetached();
Jorim Jaggi605f1902014-06-25 05:23:42 +0200878 mSystemIconsCopy.setMirroredView(null, 0, 0);
879 newParent.removeView(mSystemIconsCopy);
880 newParent.addView(systemIcons, index);
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +0200881 }
882 }
883
884 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100885 protected boolean isScrolledToBottom() {
Selim Cinekb688bf72014-06-16 16:36:51 -0700886 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
887 return true;
888 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100889 if (!isInSettings()) {
890 return mNotificationStackScroller.isScrolledToBottom();
Chris Wren64161cc2012-12-17 16:49:30 -0500891 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100892 return super.isScrolledToBottom();
893 }
894
895 @Override
896 protected int getMaxPanelHeight() {
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200897 int min = mStatusBarMinHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200898 if (mStatusBar.getBarState() != StatusBarState.KEYGUARD
899 && mNotificationStackScroller.getNotGoneChildCount() == 0) {
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200900 int minHeight = (int) ((mQsMinExpansionHeight + getOverExpansionAmount())
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200901 * HEADER_RUBBERBAND_FACTOR);
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200902 min = Math.max(min, minHeight);
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200903 }
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200904 int maxHeight;
Jorim Jaggi1e2554b2014-07-10 23:07:00 +0200905 if (mTwoFingerQsExpand || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200906 maxHeight = Math.max(calculatePanelHeightQsExpanded(), calculatePanelHeightShade());
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200907 } else {
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200908 maxHeight = calculatePanelHeightShade();
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200909 }
910 maxHeight = Math.max(maxHeight, min);
Jorim Jaggi1d480692014-05-20 19:41:58 +0200911 return maxHeight;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100912 }
913
914 private boolean isInSettings() {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200915 return mQsExpanded;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100916 }
917
918 @Override
919 protected void onHeightUpdated(float expandedHeight) {
Jorim Jaggi7d16bb12014-05-22 04:24:50 +0200920 if (!mQsExpanded) {
921 positionClockAndNotifications();
922 }
Jorim Jaggi1e2554b2014-07-10 23:07:00 +0200923 if (mTwoFingerQsExpand || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200924 && !mQsExpansionFromOverscroll) {
925 float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding()
926 + mNotificationStackScroller.getMinStackHeight()
927 + mNotificationStackScroller.getNotificationTopPadding();
928 float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
929 float t = (expandedHeight - panelHeightQsCollapsed)
930 / (panelHeightQsExpanded - panelHeightQsCollapsed);
Jorim Jaggi54045422014-07-03 18:30:40 +0200931
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200932 setQsExpansion(mQsMinExpansionHeight
Jorim Jaggi54045422014-07-03 18:30:40 +0200933 + t * (getTempQsMaxExpansion() - mQsMinExpansionHeight));
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200934 }
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200935 mNotificationStackScroller.setStackHeight(expandedHeight);
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200936 updateHeader();
Jorim Jaggi8dd95e02014-06-03 16:19:33 +0200937 updateUnlockIcon();
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200938 updateNotificationTranslucency();
939 }
940
Jorim Jaggi54045422014-07-03 18:30:40 +0200941 /**
942 * @return a temporary override of {@link #mQsMaxExpansionHeight}, which is needed when
943 * collapsing QS / the panel when QS was scrolled
944 */
945 private int getTempQsMaxExpansion() {
946 int qsTempMaxExpansion = mQsMaxExpansionHeight;
947 if (mScrollYOverride != -1) {
948 qsTempMaxExpansion -= mScrollYOverride;
949 }
950 return qsTempMaxExpansion;
951 }
952
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200953 private int calculatePanelHeightShade() {
954 int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
955 int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin
956 - mTopPaddingAdjustment;
957 maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
958 return maxHeight;
959 }
960
961 private int calculatePanelHeightQsExpanded() {
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200962 float notificationHeight = mNotificationStackScroller.getHeight()
963 - mNotificationStackScroller.getEmptyBottomMargin()
964 - mNotificationStackScroller.getTopPadding();
965 float totalHeight = mQsMaxExpansionHeight + notificationHeight
966 + mNotificationStackScroller.getNotificationTopPadding();
967 if (totalHeight > mNotificationStackScroller.getHeight()) {
968 float fullyCollapsedHeight = mQsMaxExpansionHeight
969 + mNotificationStackScroller.getMinStackHeight()
Jorim Jaggi56306252014-07-03 00:40:09 +0200970 + mNotificationStackScroller.getNotificationTopPadding()
971 - getScrollViewScrollY();
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200972 totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
973 }
Jorim Jaggi61d37f62014-07-30 17:26:55 +0200974 return (int) totalHeight;
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200975 }
976
Jorim Jaggi56306252014-07-03 00:40:09 +0200977 private int getScrollViewScrollY() {
978 if (mScrollYOverride != -1) {
979 return mScrollYOverride;
980 } else {
981 return mScrollView.getScrollY();
982 }
983 }
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200984 private void updateNotificationTranslucency() {
Jorim Jaggiad46ffef2014-07-04 18:05:06 +0200985 float alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
Jorim Jaggi5ad92c52014-07-28 21:07:32 +0200986 / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
987 + mNotificationStackScroller.getCollapseSecondCardPadding());
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200988 alpha = Math.max(0, Math.min(alpha, 1));
989 alpha = (float) Math.pow(alpha, 0.75);
Jorim Jaggi5ad92c52014-07-28 21:07:32 +0200990 if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
991 mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
992 } else if (alpha == 1f
993 && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
994 mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
995 }
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200996 mNotificationStackScroller.setAlpha(alpha);
Jorim Jaggi8dd95e02014-06-03 16:19:33 +0200997 }
998
Jorim Jaggi47c85a32014-06-05 17:25:40 +0200999 @Override
1000 protected float getOverExpansionAmount() {
1001 return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
1002 }
1003
1004 @Override
1005 protected float getOverExpansionPixels() {
1006 return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
1007 }
1008
Jorim Jaggi8dd95e02014-06-03 16:19:33 +02001009 private void updateUnlockIcon() {
1010 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
1011 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
1012 boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance;
Selim Cinekbaa23272014-07-08 18:01:07 +02001013 KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
Jorim Jaggi8dd95e02014-06-03 16:19:33 +02001014 if (active && !mUnlockIconActive && mTracking) {
Selim Cinekbaa23272014-07-08 18:01:07 +02001015 lockIcon.setImageAlpha(1.0f, true, 150, mFastOutLinearInterpolator, null);
1016 lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
1017 mFastOutLinearInterpolator);
Jorim Jaggi8dd95e02014-06-03 16:19:33 +02001018 } else if (!active && mUnlockIconActive && mTracking) {
Selim Cinekbaa23272014-07-08 18:01:07 +02001019 lockIcon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT, true,
1020 150, mFastOutLinearInterpolator, null);
1021 lockIcon.setImageScale(1.0f, true, 150,
1022 mFastOutLinearInterpolator);
Jorim Jaggi8dd95e02014-06-03 16:19:33 +02001023 }
1024 mUnlockIconActive = active;
1025 }
Jorim Jaggi457cc352014-06-02 22:47:42 +02001026 }
1027
1028 /**
1029 * Hides the header when notifications are colliding with it.
1030 */
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001031 private void updateHeader() {
Jorim Jaggi457cc352014-06-02 22:47:42 +02001032 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
1033 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001034 updateHeaderKeyguard();
Jorim Jaggi457cc352014-06-02 22:47:42 +02001035 } else {
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001036 updateHeaderShade();
Jorim Jaggi457cc352014-06-02 22:47:42 +02001037 }
1038
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001039 }
1040
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001041 private void updateHeaderShade() {
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001042 mHeader.setTranslationY(getHeaderTranslation());
1043 setQsTranslation(mQsExpansionHeight);
1044 }
1045
1046 private float getHeaderTranslation() {
1047 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
1048 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
1049 return 0;
1050 }
1051 if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
1052 if (mExpandedHeight / HEADER_RUBBERBAND_FACTOR >= mQsMinExpansionHeight) {
1053 return 0;
1054 } else {
1055 return mExpandedHeight / HEADER_RUBBERBAND_FACTOR - mQsMinExpansionHeight;
1056 }
1057 }
1058 return Math.min(0, mNotificationStackScroller.getTranslationY()) / HEADER_RUBBERBAND_FACTOR;
1059 }
1060
1061 private void updateHeaderKeyguard() {
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001062 float alpha;
1063 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
1064
1065 // When on Keyguard, we hide the header as soon as the top card of the notification
1066 // stack scroller is close enough (collision distance) to the bottom of the header.
Jorim Jaggiad46ffef2014-07-04 18:05:06 +02001067 alpha = getNotificationsTopY()
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001068 /
1069 (mQsMinExpansionHeight + mNotificationsHeaderCollideDistance);
1070
1071 } else {
1072
1073 // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
1074 // soon as we start translating the stack.
Jorim Jaggiad46ffef2014-07-04 18:05:06 +02001075 alpha = getNotificationsTopY() / mQsMinExpansionHeight;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001076 }
1077 alpha = Math.max(0, Math.min(alpha, 1));
1078 alpha = (float) Math.pow(alpha, 0.75);
Jorim Jaggi61d37f62014-07-30 17:26:55 +02001079 if (!mQsExpanded && !mKeyguardStatusBarAnimatingIn) {
1080 mKeyguardStatusBar.setAlpha(alpha);
1081 }
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001082 mKeyguardBottomArea.setAlpha(alpha);
1083 setQsTranslation(mQsExpansionHeight);
1084 }
1085
Jorim Jaggiad46ffef2014-07-04 18:05:06 +02001086 private float getNotificationsTopY() {
1087 if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
1088 return getExpandedHeight();
1089 }
1090 return mNotificationStackScroller.getNotificationsTopY();
1091 }
1092
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001093 @Override
Selim Cinek1685e632014-04-08 02:27:49 +02001094 protected void onExpandingStarted() {
1095 super.onExpandingStarted();
1096 mNotificationStackScroller.onExpansionStarted();
Selim Cinek4c6969a2014-05-26 19:22:17 +02001097 mIsExpanding = true;
Jorim Jaggi30c305c2014-07-01 23:34:41 +02001098 mQsExpandedWhenExpandingStarted = mQsExpanded;
Jorim Jaggi56306252014-07-03 00:40:09 +02001099 if (mQsExpanded) {
Jorim Jaggi56306252014-07-03 00:40:09 +02001100 onQsExpansionStarted();
1101 }
Selim Cinek1685e632014-04-08 02:27:49 +02001102 }
1103
1104 @Override
1105 protected void onExpandingFinished() {
1106 super.onExpandingFinished();
1107 mNotificationStackScroller.onExpansionStopped();
Selim Cinek4c6969a2014-05-26 19:22:17 +02001108 mIsExpanding = false;
Jorim Jaggi56306252014-07-03 00:40:09 +02001109 mScrollYOverride = -1;
Jorim Jaggie65e3102014-07-01 22:00:50 +02001110 if (mExpandedHeight == 0f) {
Jorim Jaggi4538027d2014-07-30 15:43:13 +02001111 setListening(false);
Jorim Jaggie65e3102014-07-01 22:00:50 +02001112 } else {
Jorim Jaggi4538027d2014-07-30 15:43:13 +02001113 setListening(true);
Jorim Jaggie65e3102014-07-01 22:00:50 +02001114 }
Jorim Jaggi1e2554b2014-07-10 23:07:00 +02001115 mTwoFingerQsExpand = false;
1116 mTwoFingerQsExpandPossible = false;
Jorim Jaggie65e3102014-07-01 22:00:50 +02001117 }
1118
Jorim Jaggi4538027d2014-07-30 15:43:13 +02001119 private void setListening(boolean listening) {
1120 mHeader.setListening(listening);
1121 mKeyguardStatusBar.setListening(listening);
1122 mQsPanel.setListening(listening);
1123 }
1124
Jorim Jaggie65e3102014-07-01 22:00:50 +02001125 @Override
1126 public void instantExpand() {
1127 super.instantExpand();
Jorim Jaggi4538027d2014-07-30 15:43:13 +02001128 setListening(true);
Selim Cinek1685e632014-04-08 02:27:49 +02001129 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001130
1131 @Override
Jorim Jaggi47c85a32014-06-05 17:25:40 +02001132 protected void setOverExpansion(float overExpansion, boolean isPixels) {
Jorim Jaggi1e2554b2014-07-10 23:07:00 +02001133 if (mConflictingQsExpansionGesture || mTwoFingerQsExpand) {
Jorim Jaggidc96d632014-07-01 18:48:52 +02001134 return;
1135 }
Selim Cinek1408eb52014-06-02 14:45:38 +02001136 if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
Jorim Jaggi47c85a32014-06-05 17:25:40 +02001137 mNotificationStackScroller.setOnHeightChangedListener(null);
1138 if (isPixels) {
1139 mNotificationStackScroller.setOverScrolledPixels(
1140 overExpansion, true /* onTop */, false /* animate */);
1141 } else {
1142 mNotificationStackScroller.setOverScrollAmount(
1143 overExpansion, true /* onTop */, false /* animate */);
1144 }
1145 mNotificationStackScroller.setOnHeightChangedListener(this);
Selim Cinek1408eb52014-06-02 14:45:38 +02001146 }
Selim Cinek24120a52014-05-26 10:05:42 +02001147 }
1148
1149 @Override
Jorim Jaggi282a6152014-06-02 23:37:53 +02001150 protected void onTrackingStarted() {
1151 super.onTrackingStarted();
1152 if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
1153 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
Selim Cinekbaa23272014-07-08 18:01:07 +02001154 mAfforanceHelper.animateHideLeftRightIcon();
Jorim Jaggi282a6152014-06-02 23:37:53 +02001155 }
1156 }
1157
1158 @Override
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +02001159 protected void onTrackingStopped(boolean expand) {
1160 super.onTrackingStopped(expand);
Jorim Jaggi47c85a32014-06-05 17:25:40 +02001161 if (expand) {
1162 mNotificationStackScroller.setOverScrolledPixels(
1163 0.0f, true /* onTop */, true /* animate */);
1164 }
Jorim Jaggi282a6152014-06-02 23:37:53 +02001165 if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
1166 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
Selim Cinekbaa23272014-07-08 18:01:07 +02001167 if (!mHintAnimationRunning) {
1168 mAfforanceHelper.reset(true);
1169 }
Jorim Jaggi282a6152014-06-02 23:37:53 +02001170 }
Jorim Jaggi8dd95e02014-06-03 16:19:33 +02001171 if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
1172 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
Selim Cinekbaa23272014-07-08 18:01:07 +02001173 KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
1174 lockIcon.setImageAlpha(0.0f, true, 100, mFastOutLinearInterpolator, null);
1175 lockIcon.setImageScale(2.0f, true, 100, mFastOutLinearInterpolator);
Jorim Jaggi8dd95e02014-06-03 16:19:33 +02001176 }
Selim Cinek24120a52014-05-26 10:05:42 +02001177 }
1178
Selim Cinek24120a52014-05-26 10:05:42 +02001179 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +02001180 public void onHeightChanged(ExpandableView view) {
Jorim Jaggi30c305c2014-07-01 23:34:41 +02001181
1182 // Block update if we are in quick settings and just the top padding changed
1183 // (i.e. view == null).
1184 if (view == null && mQsExpanded) {
1185 return;
1186 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001187 requestPanelHeightUpdate();
1188 }
Jorim Jaggid7daab72014-05-06 22:22:20 +02001189
1190 @Override
1191 public void onScrollChanged() {
1192 if (mQsExpanded) {
Selim Cinek1408eb52014-06-02 14:45:38 +02001193 requestScrollerTopPaddingUpdate(false /* animate */);
Jorim Jaggi56306252014-07-03 00:40:09 +02001194 requestPanelHeightUpdate();
Jorim Jaggid7daab72014-05-06 22:22:20 +02001195 }
1196 }
1197
1198 @Override
Selim Cinek4c6969a2014-05-26 19:22:17 +02001199 protected void onConfigurationChanged(Configuration newConfig) {
1200 super.onConfigurationChanged(newConfig);
Selim Cinekbaa23272014-07-08 18:01:07 +02001201 mAfforanceHelper.onConfigurationChanged();
Selim Cinek4c6969a2014-05-26 19:22:17 +02001202 }
1203
1204 @Override
Jorim Jaggid7daab72014-05-06 22:22:20 +02001205 public void onClick(View v) {
Jorim Jaggi853b0702014-07-05 04:31:14 +02001206 if (v == mHeader) {
Jorim Jaggid7daab72014-05-06 22:22:20 +02001207 onQsExpansionStarted();
1208 if (mQsExpanded) {
1209 flingSettings(0 /* vel */, false /* expand */);
1210 } else if (mQsExpansionEnabled) {
1211 flingSettings(0 /* vel */, true /* expand */);
1212 }
1213 }
1214 }
Selim Cinek4c6969a2014-05-26 19:22:17 +02001215
1216 @Override
1217 public void onAnimationToSideStarted(boolean rightPage) {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001218 boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage;
Selim Cinekbaa23272014-07-08 18:01:07 +02001219 mIsLaunchTransitionRunning = true;
1220 mLaunchAnimationEndRunnable = null;
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001221 if (start) {
Selim Cinek4c6969a2014-05-26 19:22:17 +02001222 mKeyguardBottomArea.launchPhone();
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001223 } else {
1224 mKeyguardBottomArea.launchCamera();
Selim Cinek4c6969a2014-05-26 19:22:17 +02001225 }
1226 mBlockTouches = true;
1227 }
1228
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001229 @Override
Selim Cinekbaa23272014-07-08 18:01:07 +02001230 public void onAnimationToSideEnded() {
1231 mIsLaunchTransitionRunning = false;
1232 mIsLaunchTransitionFinished = true;
1233 if (mLaunchAnimationEndRunnable != null) {
1234 mLaunchAnimationEndRunnable.run();
1235 mLaunchAnimationEndRunnable = null;
1236 }
1237 }
1238
1239 @Override
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001240 protected void onEdgeClicked(boolean right) {
1241 if ((right && getRightIcon().getVisibility() != View.VISIBLE)
1242 || (!right && getLeftIcon().getVisibility() != View.VISIBLE)) {
1243 return;
1244 }
1245 mHintAnimationRunning = true;
Selim Cinekbaa23272014-07-08 18:01:07 +02001246 mAfforanceHelper.startHintAnimation(right, new Runnable() {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001247 @Override
1248 public void run() {
1249 mHintAnimationRunning = false;
1250 mStatusBar.onHintFinished();
1251 }
1252 });
1253 boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? right : !right;
1254 if (start) {
1255 mStatusBar.onPhoneHintStarted();
1256 } else {
1257 mStatusBar.onCameraHintStarted();
1258 }
1259 }
Selim Cinek4c6969a2014-05-26 19:22:17 +02001260
1261 @Override
Jorim Jaggi93a2bb22014-06-02 19:57:28 +02001262 protected void startUnlockHintAnimation() {
1263 super.startUnlockHintAnimation();
1264 startHighlightIconAnimation(getCenterIcon());
1265 }
1266
1267 /**
1268 * Starts the highlight (making it fully opaque) animation on an icon.
1269 */
Selim Cinekbaa23272014-07-08 18:01:07 +02001270 private void startHighlightIconAnimation(final KeyguardAffordanceView icon) {
1271 icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
1272 mFastOutSlowInInterpolator, new Runnable() {
Jorim Jaggi93a2bb22014-06-02 19:57:28 +02001273 @Override
1274 public void run() {
Selim Cinekbaa23272014-07-08 18:01:07 +02001275 icon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT,
1276 true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
1277 mFastOutSlowInInterpolator, null);
Jorim Jaggi93a2bb22014-06-02 19:57:28 +02001278 }
1279 });
1280 }
1281
1282 @Override
Selim Cinek4c6969a2014-05-26 19:22:17 +02001283 public float getPageWidth() {
1284 return getWidth();
1285 }
1286
1287 @Override
Selim Cinekbaa23272014-07-08 18:01:07 +02001288 public void onSwipingStarted() {
1289 requestDisallowInterceptTouchEvent(true);
1290 mOnlyAffordanceInThisMotion = true;
Selim Cinek4c6969a2014-05-26 19:22:17 +02001291 }
1292
1293 @Override
Selim Cinekbaa23272014-07-08 18:01:07 +02001294 public KeyguardAffordanceView getLeftIcon() {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001295 return getLayoutDirection() == LAYOUT_DIRECTION_RTL
Selim Cinekbaa23272014-07-08 18:01:07 +02001296 ? mKeyguardBottomArea.getCameraView()
1297 : mKeyguardBottomArea.getPhoneView();
Selim Cinek4c6969a2014-05-26 19:22:17 +02001298 }
1299
1300 @Override
Selim Cinekbaa23272014-07-08 18:01:07 +02001301 public KeyguardAffordanceView getCenterIcon() {
Selim Cinek4c6969a2014-05-26 19:22:17 +02001302 return mKeyguardBottomArea.getLockIcon();
1303 }
1304
1305 @Override
Selim Cinekbaa23272014-07-08 18:01:07 +02001306 public KeyguardAffordanceView getRightIcon() {
Jorim Jaggib3f0a2f2014-06-02 19:29:39 +02001307 return getLayoutDirection() == LAYOUT_DIRECTION_RTL
Selim Cinekbaa23272014-07-08 18:01:07 +02001308 ? mKeyguardBottomArea.getPhoneView()
1309 : mKeyguardBottomArea.getCameraView();
Selim Cinek4c6969a2014-05-26 19:22:17 +02001310 }
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001311
1312 @Override
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +02001313 public View getLeftPreview() {
1314 return getLayoutDirection() == LAYOUT_DIRECTION_RTL
1315 ? mKeyguardBottomArea.getCameraPreview()
1316 : mKeyguardBottomArea.getPhonePreview();
1317 }
1318
1319 @Override
1320 public View getRightPreview() {
1321 return getLayoutDirection() == LAYOUT_DIRECTION_RTL
1322 ? mKeyguardBottomArea.getPhonePreview()
1323 : mKeyguardBottomArea.getCameraPreview();
1324 }
1325
1326 @Override
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001327 protected float getPeekHeight() {
1328 if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
1329 return mNotificationStackScroller.getPeekHeight();
1330 } else {
Jorim Jaggib472b3472014-06-30 19:56:24 +02001331 return mQsMinExpansionHeight * HEADER_RUBBERBAND_FACTOR;
Jorim Jaggi2580a9762014-06-25 03:08:25 +02001332 }
1333 }
Jorim Jaggi30c305c2014-07-01 23:34:41 +02001334
1335 @Override
1336 protected float getCannedFlingDurationFactor() {
1337 if (mQsExpanded) {
1338 return 0.7f;
1339 } else {
1340 return 0.6f;
1341 }
1342 }
1343
1344 @Override
Jorim Jaggi4b04a3a2014-07-28 17:43:56 +02001345 protected boolean fullyExpandedClearAllVisible() {
1346 return mNotificationStackScroller.isDismissViewNotGone()
1347 && mNotificationStackScroller.isScrolledToBottom();
1348 }
1349
1350 @Override
1351 protected boolean isClearAllVisible() {
1352 return mNotificationStackScroller.isDismissViewVisible();
1353 }
1354
1355 @Override
1356 protected int getClearAllHeight() {
1357 return mNotificationStackScroller.getDismissViewHeight();
1358 }
1359
1360 @Override
Jorim Jaggi30c305c2014-07-01 23:34:41 +02001361 protected boolean isTrackingBlocked() {
1362 return mConflictingQsExpansionGesture && mQsExpanded;
1363 }
Jorim Jaggi54045422014-07-03 18:30:40 +02001364
1365 public void notifyVisibleChildrenChanged() {
1366 if (mNotificationStackScroller.getNotGoneChildCount() != 0) {
1367 mReserveNotificationSpace.setVisibility(View.VISIBLE);
1368 } else {
1369 mReserveNotificationSpace.setVisibility(View.GONE);
1370 }
1371 }
Jorim Jaggibd003102014-07-04 00:02:42 +02001372
1373 public boolean isQsExpanded() {
1374 return mQsExpanded;
1375 }
Jorim Jaggi853b0702014-07-05 04:31:14 +02001376
1377 @Override
1378 public boolean shouldDelayChildPressedState() {
1379 return true;
1380 }
Selim Cinekbaa23272014-07-08 18:01:07 +02001381
1382 public boolean isLaunchTransitionFinished() {
1383 return mIsLaunchTransitionFinished;
1384 }
1385
1386 public boolean isLaunchTransitionRunning() {
1387 return mIsLaunchTransitionRunning;
1388 }
1389
1390 public void setLaunchTransitionEndRunnable(Runnable r) {
1391 mLaunchAnimationEndRunnable = r;
1392 }
Jorim Jaggi48bc36a2014-07-25 23:16:04 +02001393
1394 public void setEmptyDragAmount(float amount) {
1395 float factor = 1f;
1396 if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
1397 factor = 0.6f;
Christoph Studerc8db24b2014-07-25 17:50:30 +02001398 } else if (!mStatusBar.hasActiveNotifications()) {
Jorim Jaggi48bc36a2014-07-25 23:16:04 +02001399 factor = 0.4f;
1400 }
1401 mEmptyDragAmount = amount * factor;
1402 positionClockAndNotifications();
1403 }
Jorim Jaggi61d37f62014-07-30 17:26:55 +02001404
1405 private static float interpolate(float t, float start, float end) {
1406 return (1 - t) * start + t * end;
1407 }
Daniel Sandler08d05e32012-08-08 16:39:54 -04001408}