blob: 6132ed22ebae8e3a87a6c97d96a9ba0e00c9474c [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;
21import android.animation.ValueAnimator;
Daniel Sandler08d05e32012-08-08 16:39:54 -040022import android.content.Context;
23import android.util.AttributeSet;
Daniel Sandler040c2e42012-10-17 00:56:33 -040024import android.view.MotionEvent;
Jorim Jaggid7daab72014-05-06 22:22:20 +020025import android.view.VelocityTracker;
Daniel Sandler13522a22012-09-27 14:46:58 -040026import android.view.View;
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +020027import android.view.ViewGroup;
Casey Burkhardt23b0a4e2013-04-29 12:18:32 -070028import android.view.accessibility.AccessibilityEvent;
Jorim Jaggid7daab72014-05-06 22:22:20 +020029import android.view.animation.AnimationUtils;
30import android.view.animation.Interpolator;
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +020031import android.widget.LinearLayout;
Daniel Sandler13522a22012-09-27 14:46:58 -040032
Chet Haase4d179dc2012-08-22 07:14:42 -070033import com.android.systemui.R;
Jorim Jaggibe565df2014-04-28 17:51:23 +020034import com.android.systemui.statusbar.ExpandableView;
Daniel Sandler151f00d2012-10-02 22:33:08 -040035import com.android.systemui.statusbar.GestureRecorder;
Jorim Jaggiecbab362014-04-23 16:13:15 +020036import com.android.systemui.statusbar.StatusBarState;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010037import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Daniel Sandler08d05e32012-08-08 16:39:54 -040038
Jorim Jaggibe565df2014-04-28 17:51:23 +020039public class NotificationPanelView extends PanelView implements
Jorim Jaggid7daab72014-05-06 22:22:20 +020040 ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
41 View.OnClickListener {
Chris Wren64161cc2012-12-17 16:49:30 -050042 public static final boolean DEBUG_GESTURES = true;
Jorim Jaggid7daab72014-05-06 22:22:20 +020043 private static final int EXPANSION_ANIMATION_LENGTH = 375;
Chet Haase4d179dc2012-08-22 07:14:42 -070044
Daniel Sandler040c2e42012-10-17 00:56:33 -040045 PhoneStatusBar mStatusBar;
Jorim Jaggid7daab72014-05-06 22:22:20 +020046 private StatusBarHeaderView mHeader;
47 private QuickSettingsContainerView mQsContainer;
John Spurlock73203eb2014-04-15 16:14:46 -040048 private View mKeyguardStatusView;
Jorim Jaggid7daab72014-05-06 22:22:20 +020049 private ObservableScrollView mScrollView;
50 private View mStackScrollerContainer;
John Spurlock73203eb2014-04-15 16:14:46 -040051
Selim Cinekb6d85eb2014-03-28 20:21:01 +010052 private NotificationStackScrollLayout mNotificationStackScroller;
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +020053 private int mNotificationTopPadding;
Jorim Jaggi98fb09c2014-05-01 22:40:56 +020054 private boolean mAnimateNextTopPaddingChange;
Chet Haase4d179dc2012-08-22 07:14:42 -070055
Jorim Jaggid7daab72014-05-06 22:22:20 +020056 private Interpolator mExpansionInterpolator;
57
58 private int mTrackingPointer;
59 private VelocityTracker mVelocityTracker;
60 private boolean mTracking;
Jorim Jaggifc1a8242014-05-12 19:30:05 +020061
62 /**
63 * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
64 * intercepted yet.
65 */
66 private boolean mIntercepting;
Jorim Jaggid7daab72014-05-06 22:22:20 +020067 private boolean mQsExpanded;
68 private float mInitialHeightOnTouch;
69 private float mInitialTouchX;
70 private float mInitialTouchY;
Jorim Jaggifc1a8242014-05-12 19:30:05 +020071 private float mLastTouchX;
72 private float mLastTouchY;
Jorim Jaggid7daab72014-05-06 22:22:20 +020073 private float mQsExpansionHeight;
74 private int mQsMinExpansionHeight;
75 private int mQsMaxExpansionHeight;
76 private int mMinStackHeight;
77 private float mNotificationTranslation;
78 private int mStackScrollerIntrinsicPadding;
79 private boolean mQsExpansionEnabled = true;
80 private ValueAnimator mQsExpansionAnimator;
81
Daniel Sandler08d05e32012-08-08 16:39:54 -040082 public NotificationPanelView(Context context, AttributeSet attrs) {
83 super(context, attrs);
Daniel Sandler13522a22012-09-27 14:46:58 -040084 }
Chet Haase4d179dc2012-08-22 07:14:42 -070085
Daniel Sandler040c2e42012-10-17 00:56:33 -040086 public void setStatusBar(PhoneStatusBar bar) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +010087 if (mStatusBar != null) {
88 mStatusBar.setOnFlipRunnable(null);
89 }
Daniel Sandler040c2e42012-10-17 00:56:33 -040090 mStatusBar = bar;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010091 if (bar != null) {
92 mStatusBar.setOnFlipRunnable(new Runnable() {
93 @Override
94 public void run() {
95 requestPanelHeightUpdate();
96 }
97 });
98 }
Daniel Sandler040c2e42012-10-17 00:56:33 -040099 }
100
Daniel Sandler13522a22012-09-27 14:46:58 -0400101 @Override
102 protected void onFinishInflate() {
103 super.onFinishInflate();
Jorim Jaggid7daab72014-05-06 22:22:20 +0200104 mHeader = (StatusBarHeaderView) findViewById(R.id.header);
105 mHeader.getBackgroundView().setOnClickListener(this);
Jorim Jaggi3d878be2014-05-10 03:22:32 +0200106 mHeader.setOverlayParent(this);
John Spurlock73203eb2014-04-15 16:14:46 -0400107 mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200108 mStackScrollerContainer = findViewById(R.id.notification_container_parent);
109 mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
110 mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
111 mScrollView.setListener(this);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100112 mNotificationStackScroller = (NotificationStackScrollLayout)
113 findViewById(R.id.notification_stack_scroller);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200114 mNotificationStackScroller.setOnHeightChangedListener(this);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200115 mNotificationTopPadding = getResources().getDimensionPixelSize(
116 R.dimen.notifications_top_padding);
Jorim Jaggid7daab72014-05-06 22:22:20 +0200117 mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
118 mExpansionInterpolator = AnimationUtils.loadInterpolator(
119 getContext(), android.R.interpolator.fast_out_slow_in);
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200120 }
121
122 @Override
123 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
124 super.onLayout(changed, left, top, right, bottom);
125 int keyguardBottomMargin =
126 ((MarginLayoutParams) mKeyguardStatusView.getLayoutParams()).bottomMargin;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200127 if (!mQsExpanded) {
128 mStackScrollerIntrinsicPadding = mStatusBar.getBarState() == StatusBarState.KEYGUARD
129 ? mKeyguardStatusView.getBottom() + keyguardBottomMargin
130 : mHeader.getBottom() + mNotificationTopPadding;
131 mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
132 mAnimateNextTopPaddingChange);
133 mAnimateNextTopPaddingChange = false;
134 }
135
136 // Calculate quick setting heights.
137 mQsMinExpansionHeight = mHeader.getCollapsedHeight();
138 mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
139 if (mQsExpansionHeight == 0) {
140 mQsExpansionHeight = mQsMinExpansionHeight;
141 }
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200142 }
143
144 public void animateNextTopPaddingChange() {
145 mAnimateNextTopPaddingChange = true;
146 requestLayout();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400147 }
148
Jorim Jaggid7daab72014-05-06 22:22:20 +0200149 /**
150 * @return Whether Quick Settings are currently expanded.
151 */
152 public boolean isQsExpanded() {
153 return mQsExpanded;
154 }
155
156 public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
157 mQsExpansionEnabled = qsExpansionEnabled;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200158 }
159
160 public void closeQs() {
161 cancelAnimation();
162 setQsExpansion(mQsMinExpansionHeight);
163 }
164
165 public void openQs() {
166 cancelAnimation();
167 if (mQsExpansionEnabled) {
168 setQsExpansion(mQsMaxExpansionHeight);
169 }
170 }
171
Daniel Sandler08d05e32012-08-08 16:39:54 -0400172 @Override
173 public void fling(float vel, boolean always) {
Daniel Sandler151f00d2012-10-02 22:33:08 -0400174 GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
175 if (gr != null) {
176 gr.tag(
177 "fling " + ((vel > 0) ? "open" : "closed"),
178 "notifications,v=" + vel);
179 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400180 super.fling(vel, always);
181 }
Chet Haase4d179dc2012-08-22 07:14:42 -0700182
Casey Burkhardt23b0a4e2013-04-29 12:18:32 -0700183 @Override
184 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
185 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
186 event.getText()
187 .add(getContext().getString(R.string.accessibility_desc_notification_shade));
188 return true;
189 }
190
191 return super.dispatchPopulateAccessibilityEvent(event);
192 }
193
Daniel Sandler040c2e42012-10-17 00:56:33 -0400194 @Override
John Spurlock73203eb2014-04-15 16:14:46 -0400195 public boolean onInterceptTouchEvent(MotionEvent event) {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200196 int pointerIndex = event.findPointerIndex(mTrackingPointer);
197 if (pointerIndex < 0) {
198 pointerIndex = 0;
199 mTrackingPointer = event.getPointerId(pointerIndex);
John Spurlock73203eb2014-04-15 16:14:46 -0400200 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200201 final float x = event.getX(pointerIndex);
202 final float y = event.getY(pointerIndex);
203
204 switch (event.getActionMasked()) {
205 case MotionEvent.ACTION_DOWN:
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200206 mIntercepting = true;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200207 mInitialTouchY = y;
208 mInitialTouchX = x;
209 initVelocityTracker();
210 trackMovement(event);
211 if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
212 getParent().requestDisallowInterceptTouchEvent(true);
213 }
214 break;
215 case MotionEvent.ACTION_POINTER_UP:
216 final int upPointer = event.getPointerId(event.getActionIndex());
217 if (mTrackingPointer == upPointer) {
218 // gesture is ongoing, find a new pointer to track
219 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
220 mTrackingPointer = event.getPointerId(newIndex);
221 mInitialTouchX = event.getX(newIndex);
222 mInitialTouchY = event.getY(newIndex);
223 }
224 break;
225
226 case MotionEvent.ACTION_MOVE:
227 final float h = y - mInitialTouchY;
228 trackMovement(event);
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200229 if (mTracking) {
230
231 // Already tracking because onOverscrolled was called. We need to update here
232 // so we don't stop for a frame until the next touch event gets handled in
233 // onTouchEvent.
234 setQsExpansion(h + mInitialHeightOnTouch);
235 trackMovement(event);
236 mIntercepting = false;
237 return true;
238 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200239 if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
240 && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
241 onQsExpansionStarted();
242 mInitialHeightOnTouch = mQsExpansionHeight;
243 mInitialTouchY = y;
244 mInitialTouchX = x;
245 mTracking = true;
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200246 mIntercepting = false;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200247 return true;
248 }
249 break;
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200250
251 case MotionEvent.ACTION_CANCEL:
252 case MotionEvent.ACTION_UP:
253 mIntercepting = false;
254 break;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200255 }
256 return !mQsExpanded && super.onInterceptTouchEvent(event);
John Spurlock73203eb2014-04-15 16:14:46 -0400257 }
258
259 @Override
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200260 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
261
262 // Block request so we can still intercept the scrolling when QS is expanded.
263 if (!mQsExpanded) {
264 super.requestDisallowInterceptTouchEvent(disallowIntercept);
265 }
266 }
267
268 @Override
Daniel Sandler040c2e42012-10-17 00:56:33 -0400269 public boolean onTouchEvent(MotionEvent event) {
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100270 // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
271 // implementation.
Jorim Jaggid7daab72014-05-06 22:22:20 +0200272 if (mTracking) {
273 int pointerIndex = event.findPointerIndex(mTrackingPointer);
274 if (pointerIndex < 0) {
275 pointerIndex = 0;
276 mTrackingPointer = event.getPointerId(pointerIndex);
277 }
278 final float y = event.getY(pointerIndex);
279 final float x = event.getX(pointerIndex);
280
281 switch (event.getActionMasked()) {
282 case MotionEvent.ACTION_DOWN:
283 mTracking = true;
284 mInitialTouchY = y;
285 mInitialTouchX = x;
286 onQsExpansionStarted();
287 mInitialHeightOnTouch = mQsExpansionHeight;
288 initVelocityTracker();
289 trackMovement(event);
290 break;
291
292 case MotionEvent.ACTION_POINTER_UP:
293 final int upPointer = event.getPointerId(event.getActionIndex());
294 if (mTrackingPointer == upPointer) {
295 // gesture is ongoing, find a new pointer to track
296 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
297 final float newY = event.getY(newIndex);
298 final float newX = event.getX(newIndex);
299 mTrackingPointer = event.getPointerId(newIndex);
300 mInitialHeightOnTouch = mQsExpansionHeight;
301 mInitialTouchY = newY;
302 mInitialTouchX = newX;
303 }
304 break;
305
306 case MotionEvent.ACTION_MOVE:
307 final float h = y - mInitialTouchY;
308 setQsExpansion(h + mInitialHeightOnTouch);
309 trackMovement(event);
310 break;
311
312 case MotionEvent.ACTION_UP:
313 case MotionEvent.ACTION_CANCEL:
314 mTracking = false;
315 mTrackingPointer = -1;
316 trackMovement(event);
317
318 float vel = getCurrentVelocity();
319
320 // TODO: Better logic whether we should expand or not.
321 flingSettings(vel, vel > 0);
322
323 if (mVelocityTracker != null) {
324 mVelocityTracker.recycle();
325 mVelocityTracker = null;
326 }
327 break;
John Spurlock73203eb2014-04-15 16:14:46 -0400328 }
329 return true;
330 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200331
332 // Consume touch events when QS are expanded.
333 return mQsExpanded || super.onTouchEvent(event);
334 }
335
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200336 @Override
337 public void onOverscrolled(int amount) {
338 if (mIntercepting) {
339 onQsExpansionStarted(amount);
340 mInitialHeightOnTouch = mQsExpansionHeight;
341 mInitialTouchY = mLastTouchY;
342 mInitialTouchX = mLastTouchX;
343 mTracking = true;
344 }
345 }
346
Jorim Jaggid7daab72014-05-06 22:22:20 +0200347 private void onQsExpansionStarted() {
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200348 onQsExpansionStarted(0);
349 }
350
351 private void onQsExpansionStarted(int overscrollAmount) {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200352 cancelAnimation();
353
354 // Reset scroll position and apply that position to the expanded height.
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200355 float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount;
Jorim Jaggid7daab72014-05-06 22:22:20 +0200356 mScrollView.scrollTo(0, 0);
357 setQsExpansion(height);
358 }
359
360 private void expandQs() {
361 mHeader.setExpanded(true);
362 mNotificationStackScroller.setEnabled(false);
363 mScrollView.setVisibility(View.VISIBLE);
364 mQsExpanded = true;
365 }
366
367 private void collapseQs() {
368 mHeader.setExpanded(false);
369 mNotificationStackScroller.setEnabled(true);
370 mScrollView.setVisibility(View.INVISIBLE);
371 mQsExpanded = false;
372 }
373
374 private void setQsExpansion(float height) {
375 height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
376 if (height > mQsMinExpansionHeight && !mQsExpanded) {
377 expandQs();
378 } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
379 collapseQs();
John Spurlock73203eb2014-04-15 16:14:46 -0400380 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200381 mQsExpansionHeight = height;
382 mHeader.setExpansion(height);
383 setQsTranslation(height);
384 setQsStackScrollerPadding(height);
385 }
386
387 private void setQsTranslation(float height) {
388 mQsContainer.setY(height - mQsContainer.getHeight());
389 }
390
391 private void setQsStackScrollerPadding(float height) {
392 float start = height - mScrollView.getScrollY() + mNotificationTopPadding;
393 float stackHeight = mNotificationStackScroller.getHeight() - start;
394 if (stackHeight <= mMinStackHeight) {
395 float overflow = mMinStackHeight - stackHeight;
396 stackHeight = mMinStackHeight;
397 start = mNotificationStackScroller.getHeight() - stackHeight;
398 mNotificationStackScroller.setTranslationY(overflow);
399 mNotificationTranslation = overflow + mScrollView.getScrollY();
400 } else {
401 mNotificationStackScroller.setTranslationY(0);
402 mNotificationTranslation = mScrollView.getScrollY();
403 }
404 mNotificationStackScroller.setTopPadding(clampQsStackScrollerPadding((int) start), false);
405 }
406
407 private int clampQsStackScrollerPadding(int desiredPadding) {
408 return Math.max(desiredPadding, mStackScrollerIntrinsicPadding);
409 }
410
411 private void trackMovement(MotionEvent event) {
412 if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
Jorim Jaggifc1a8242014-05-12 19:30:05 +0200413 mLastTouchX = event.getX();
414 mLastTouchY = event.getY();
Jorim Jaggid7daab72014-05-06 22:22:20 +0200415 }
416
417 private void initVelocityTracker() {
418 if (mVelocityTracker != null) {
419 mVelocityTracker.recycle();
420 }
421 mVelocityTracker = VelocityTracker.obtain();
422 }
423
424 private float getCurrentVelocity() {
425 if (mVelocityTracker == null) {
426 return 0;
427 }
428 mVelocityTracker.computeCurrentVelocity(1000);
429 return mVelocityTracker.getYVelocity();
430 }
431
432 private void cancelAnimation() {
433 if (mQsExpansionAnimator != null) {
434 mQsExpansionAnimator.cancel();
435 }
436 }
437 private void flingSettings(float vel, boolean expand) {
438
439 // TODO: Actually use velocity.
440
441 float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
442 ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
443 animator.setDuration(EXPANSION_ANIMATION_LENGTH);
444 animator.setInterpolator(mExpansionInterpolator);
445 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
446 @Override
447 public void onAnimationUpdate(ValueAnimator animation) {
448 setQsExpansion((Float) animation.getAnimatedValue());
449 }
450 });
451 animator.addListener(new AnimatorListenerAdapter() {
452 @Override
453 public void onAnimationEnd(Animator animation) {
454 mQsExpansionAnimator = null;
455 }
456 });
457 animator.start();
458 mQsExpansionAnimator = animator;
459 }
460
461 /**
462 * @return Whether we should intercept a gesture to open Quick Settings.
463 */
464 private boolean shouldIntercept(float x, float y, float yDiff) {
465 if (!mQsExpansionEnabled) {
466 return false;
467 }
Jorim Jaggibc5f4962014-05-12 00:00:52 +0200468 boolean onHeader = x >= mHeader.getLeft() && x <= mHeader.getRight()
469 && y >= mHeader.getTop() && y <= mHeader.getBottom();
Jorim Jaggid7daab72014-05-06 22:22:20 +0200470 if (mQsExpanded) {
471 return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
472 } else {
473 return onHeader;
474 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100475 }
476
477 @Override
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +0200478 public void setVisibility(int visibility) {
479 int oldVisibility = getVisibility();
480 super.setVisibility(visibility);
481 if (visibility != oldVisibility) {
482 reparentStatusIcons(visibility == VISIBLE);
483 }
484 }
485
486 /**
487 * When the notification panel gets expanded, we need to move the status icons in the header
488 * card.
489 */
490 private void reparentStatusIcons(boolean toHeader) {
491 if (mStatusBar == null) {
492 return;
493 }
494 LinearLayout systemIcons = mStatusBar.getSystemIcons();
495 if (systemIcons.getParent() != null) {
496 ((ViewGroup) systemIcons.getParent()).removeView(systemIcons);
497 }
498 if (toHeader) {
499 mHeader.attachSystemIcons(systemIcons);
500 } else {
Jorim Jaggi13c1b1f2014-05-11 21:55:00 +0200501 mHeader.onSystemIconsDetached();
Jorim Jaggi0d74eeb2014-05-09 22:05:24 +0200502 mStatusBar.reattachSystemIcons();
503 }
504 }
505
506 @Override
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100507 protected boolean isScrolledToBottom() {
508 if (!isInSettings()) {
509 return mNotificationStackScroller.isScrolledToBottom();
Chris Wren64161cc2012-12-17 16:49:30 -0500510 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100511 return super.isScrolledToBottom();
512 }
513
514 @Override
515 protected int getMaxPanelHeight() {
516 if (!isInSettings()) {
517 int maxPanelHeight = super.getMaxPanelHeight();
Jorim Jaggid7daab72014-05-06 22:22:20 +0200518 int notificationMarginBottom = mStackScrollerContainer.getPaddingBottom();
519 int emptyBottomMargin = notificationMarginBottom
520 + mNotificationStackScroller.getEmptyBottomMargin();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100521 return maxPanelHeight - emptyBottomMargin;
Daniel Sandler040c2e42012-10-17 00:56:33 -0400522 }
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100523 return super.getMaxPanelHeight();
524 }
525
526 private boolean isInSettings() {
Jorim Jaggid7daab72014-05-06 22:22:20 +0200527 return mQsExpanded;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100528 }
529
530 @Override
531 protected void onHeightUpdated(float expandedHeight) {
Jorim Jaggi8c1a44b2014-04-29 19:04:02 +0200532 mNotificationStackScroller.setStackHeight(expandedHeight);
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100533 }
534
535 @Override
536 protected int getDesiredMeasureHeight() {
537 return mMaxPanelHeight;
Daniel Sandler040c2e42012-10-17 00:56:33 -0400538 }
Selim Cinek1685e632014-04-08 02:27:49 +0200539
540 @Override
541 protected void onExpandingStarted() {
542 super.onExpandingStarted();
543 mNotificationStackScroller.onExpansionStarted();
544 }
545
546 @Override
547 protected void onExpandingFinished() {
548 super.onExpandingFinished();
549 mNotificationStackScroller.onExpansionStopped();
550 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200551
552 @Override
553 public void onHeightChanged(ExpandableView view) {
554 requestPanelHeightUpdate();
555 }
Jorim Jaggid7daab72014-05-06 22:22:20 +0200556
557 @Override
558 public void onScrollChanged() {
559 if (mQsExpanded) {
560 mNotificationStackScroller.setTranslationY(
561 mNotificationTranslation - mScrollView.getScrollY());
562 }
563 }
564
565 @Override
566 public void onClick(View v) {
567 if (v == mHeader.getBackgroundView()) {
568 onQsExpansionStarted();
569 if (mQsExpanded) {
570 flingSettings(0 /* vel */, false /* expand */);
571 } else if (mQsExpansionEnabled) {
572 flingSettings(0 /* vel */, true /* expand */);
573 }
574 }
575 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400576}