diff options
author | Selim Cinek <cinek@google.com> | 2014-03-28 20:21:01 +0100 |
---|---|---|
committer | Selim Cinek <cinek@google.com> | 2014-04-03 18:59:37 +0200 |
commit | b6d85ebfe4f9f5d3b7d7ab7b6123af02a0deb516 (patch) | |
tree | 1be4ffe55781f9fa02602f97d856e84af11abc14 /packages | |
parent | 10a5ec230f41fcfba7bdd0acb32f7bb113eba3da (diff) | |
download | frameworks_base-b6d85ebfe4f9f5d3b7d7ab7b6123af02a0deb516.zip frameworks_base-b6d85ebfe4f9f5d3b7d7ab7b6123af02a0deb516.tar.gz frameworks_base-b6d85ebfe4f9f5d3b7d7ab7b6123af02a0deb516.tar.bz2 |
Enabled the new notification shade and improved expanding logic
Made the NotificationStackScroller now the default and only shade.
When the notification shade is expanded, the NotificationStackScroller
now also expands revealing the notifications.
Change-Id: If989ed848f684b3ac4e687d9642289db4599553b
Diffstat (limited to 'packages')
10 files changed, 536 insertions, 527 deletions
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 1da66bb..a7ec064 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -24,19 +24,10 @@ android:id="@+id/notification_panel" android:layout_width="0dp" android:layout_height="wrap_content" - android:background="@drawable/notification_panel_bg" android:paddingTop="@dimen/notification_panel_padding_top" android:layout_marginStart="@dimen/notification_panel_margin_left" > - <View - android:id="@+id/handle" - android:layout_width="match_parent" - android:layout_height="@dimen/close_handle_height" - android:background="@drawable/status_bar_close" - android:visibility="invisible" - /> - <include layout="@layout/carrier_label" android:layout_height="@dimen/carrier_label_height" @@ -69,6 +60,7 @@ /> <FrameLayout + android:id="@+id/notification_container_parent" android:layout_width="match_parent" android:layout_height="wrap_content" > @@ -77,21 +69,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - - <ScrollView - android:id="@+id/scroll" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:fadingEdge="none" - android:overScrollMode="ifContentScrolls" - > - <com.android.systemui.statusbar.policy.NotificationRowLayout - android:id="@+id/latestItems" - android:layout_width="match_parent" - android:layout_height="wrap_content" - systemui:rowHeight="@dimen/notification_row_min_height" - /> - </ScrollView> <com.android.systemui.statusbar.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller" diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 90b0c49..1832d37 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -34,6 +34,8 @@ import android.view.View.OnClickListener; import android.view.ViewConfiguration; import android.view.ViewGroup; +import com.android.systemui.statusbar.policy.ScrollAdapter; + public class ExpandHelper implements Gefingerpoken, OnClickListener { public interface Callback { View getChildAtRawPosition(float x, float y); @@ -609,19 +611,5 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { } mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM); } - - public interface ScrollAdapter { - - /** - * @return Whether the view returned by {@link #getHostView()} is scrolled to the top - * and can therefore be expanded by a single finger drag - */ - public boolean isScrolledToTop(); - - /** - * @return The view in which the scrolling is performed - */ - public View getHostView(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index e5e287d..f349036 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -41,7 +41,6 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -78,6 +77,7 @@ import com.android.systemui.RecentsComponent; import com.android.systemui.SearchPanelView; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.phone.KeyguardTouchDelegate; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import java.util.ArrayList; import java.util.Locale; @@ -98,8 +98,6 @@ public abstract class BaseStatusBar extends SystemUI implements protected static final int MSG_HIDE_HEADS_UP = 1027; protected static final int MSG_ESCALATE_HEADS_UP = 1028; - public static final boolean ENABLE_NOTIFICATION_STACK = SystemProperties - .getBoolean("persist.notifications.use_stack", false); protected static final boolean ENABLE_HEADS_UP = true; // scores above this threshold should be displayed in heads up mode. protected static final int INTERRUPTION_THRESHOLD = 10; @@ -120,7 +118,7 @@ public abstract class BaseStatusBar extends SystemUI implements // all notifications protected NotificationData mNotificationData = new NotificationData(); - protected ViewGroup mPile; + protected NotificationStackScrollLayout mStackScroller; protected NotificationData.Entry mInterruptingNotificationEntry; protected long mInterruptingNotificationTime; @@ -1033,7 +1031,7 @@ public abstract class BaseStatusBar extends SystemUI implements } // Construct the expanded view. NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView); - if (!inflateViews(entry, mPile)) { + if (!inflateViews(entry, mStackScroller)) { handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " + notification); return null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 6be6d4d..2d2f2f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -17,45 +17,51 @@ package com.android.systemui.statusbar.phone; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.util.EventLog; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; -import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; public class NotificationPanelView extends PanelView { public static final boolean DEBUG_GESTURES = true; - Drawable mHandleBar; - int mHandleBarHeight; - View mHandleView; - int mFingers; PhoneStatusBar mStatusBar; - boolean mOkToFlip; + private NotificationStackScrollLayout mNotificationStackScroller; + private int[] mTempLocation = new int[2]; + private int[] mTempChildLocation = new int[2]; + private View mNotificationParent; + public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); } public void setStatusBar(PhoneStatusBar bar) { + if (mStatusBar != null) { + mStatusBar.setOnFlipRunnable(null); + } mStatusBar = bar; + if (bar != null) { + mStatusBar.setOnFlipRunnable(new Runnable() { + @Override + public void run() { + requestPanelHeightUpdate(); + } + }); + } } @Override protected void onFinishInflate() { super.onFinishInflate(); - Resources resources = getContext().getResources(); - mHandleBar = resources.getDrawable(R.drawable.status_bar_close); - mHandleBarHeight = resources.getDimensionPixelSize(R.dimen.close_handle_height); - mHandleView = findViewById(R.id.handle); + mNotificationStackScroller = (NotificationStackScrollLayout) + findViewById(R.id.notification_stack_scroller); + mNotificationParent = findViewById(R.id.notification_container_parent); } @Override @@ -80,61 +86,86 @@ public class NotificationPanelView extends PanelView { return super.dispatchPopulateAccessibilityEvent(event); } - // We draw the handle ourselves so that it's always glued to the bottom of the window. + /** + * Gets the relative position of a view on the screen in regard to this view. + * + * @param requestedView the view we want to find the relative position for + * @return + */ + private int getRelativeTop(View requestedView) { + getLocationOnScreen(mTempLocation); + requestedView.getLocationOnScreen(mTempChildLocation); + return mTempChildLocation[1] - mTempLocation[1]; + } + @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (changed) { - final int pl = getPaddingLeft(); - final int pr = getPaddingRight(); - mHandleBar.setBounds(pl, 0, getWidth() - pr, (int) mHandleBarHeight); - } + public boolean onTouchEvent(MotionEvent event) { + // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference + // implementation. + return super.onTouchEvent(event); } @Override - public void draw(Canvas canvas) { - super.draw(canvas); - final int off = (int) (getHeight() - mHandleBarHeight - getPaddingBottom()); - canvas.translate(0, off); - mHandleBar.setState(mHandleView.getDrawableState()); - mHandleBar.draw(canvas); - canvas.translate(0, -off); + protected boolean isScrolledToBottom() { + if (!isInSettings()) { + return mNotificationStackScroller.isScrolledToBottom(); + } + return super.isScrolledToBottom(); } @Override - public boolean onTouchEvent(MotionEvent event) { - if (DEBUG_GESTURES) { - if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { - EventLog.writeEvent(EventLogTags.SYSUI_NOTIFICATIONPANEL_TOUCH, - event.getActionMasked(), (int) event.getX(), (int) event.getY()); - } + protected int getMaxPanelHeight() { + if (!isInSettings()) { + int maxPanelHeight = super.getMaxPanelHeight(); + int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); + return maxPanelHeight - emptyBottomMargin; } - if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mOkToFlip = getExpandedHeight() == 0; - break; - case MotionEvent.ACTION_POINTER_DOWN: - if (mOkToFlip) { - float miny = event.getY(0); - float maxy = miny; - for (int i=1; i<event.getPointerCount(); i++) { - final float y = event.getY(i); - if (y < miny) miny = y; - if (y > maxy) maxy = y; - } - if (maxy - miny < mHandleBarHeight) { - if (getMeasuredHeight() < mHandleBarHeight) { - mStatusBar.switchToSettings(); - } else { - mStatusBar.flipToSettings(); - } - mOkToFlip = false; - } - } - break; - } + return super.getMaxPanelHeight(); + } + + private boolean isInSettings() { + return mStatusBar != null && mStatusBar.isFlippedToSettings(); + } + + @Override + protected void onHeightUpdated(float expandedHeight) { + updateNotificationStackHeight(expandedHeight); + } + + /** + * Update the height of the {@link #mNotificationStackScroller} to the new expanded height. + * This is much more efficient than doing it over the layout pass. + * + * @param expandedHeight the new expanded height + */ + private void updateNotificationStackHeight(float expandedHeight) { + float childOffset = getRelativeTop(mNotificationStackScroller) + - mNotificationParent.getTranslationY(); + int newStackHeight = (int) (expandedHeight - childOffset); + int itemHeight = mNotificationStackScroller.getItemHeight(); + int bottomStackPeekSize = mNotificationStackScroller.getBottomStackPeekSize(); + int minStackHeight = itemHeight + bottomStackPeekSize; + if (newStackHeight >= minStackHeight) { + mNotificationParent.setTranslationY(0); + mNotificationStackScroller.setCurrentStackHeight(newStackHeight); + } else { + + // We did not reach the position yet where we actually start growing, + // so we translate the stack upwards. + int translationY = (newStackHeight - minStackHeight); + // A slight parallax effect is introduced in order for the stack to catch up with + // the top card. + float partiallyThere = (float) newStackHeight / minStackHeight; + partiallyThere = Math.max(0, partiallyThere); + translationY += (1 - partiallyThere) * bottomStackPeekSize; + mNotificationParent.setTranslationY(translationY); + mNotificationStackScroller.setCurrentStackHeight( + (int) (expandedHeight - (childOffset + translationY))); } - return mHandleView.dispatchTouchEvent(event); + } + + @Override + protected int getDesiredMeasureHeight() { + return mMaxPanelHeight; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 4b2c3e1..3c8af30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -25,6 +25,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.widget.FrameLayout; import com.android.systemui.R; @@ -69,7 +70,7 @@ public class PanelView extends FrameLayout { private View mHandleView; private float mPeekHeight; - private float mTouchOffset; + private float mInitialOffsetOnTouch; private float mExpandedFraction = 0; private float mExpandedHeight = 0; private boolean mJustPeeked; @@ -77,6 +78,7 @@ public class PanelView extends FrameLayout { private boolean mRubberbanding; private boolean mTracking; private int mTrackingPointer; + private int mTouchSlop; private TimeAnimator mTimeAnimator; private ObjectAnimator mPeekAnimator; @@ -198,7 +200,6 @@ public class PanelView extends FrameLayout { } } - private int[] mAbsPos = new int[2]; PanelBar mBar; private final TimeListener mAnimationCallback = new TimeListener() { @@ -220,7 +221,7 @@ public class PanelView extends FrameLayout { }; private float mVel, mAccel; - private int mFullHeight = 0; + protected int mMaxPanelHeight = 0; private String mViewName; protected float mInitialTouchY; protected float mFinalTouchY; @@ -253,13 +254,13 @@ public class PanelView extends FrameLayout { mTimeAnimator.start(); mRubberbanding = mRubberbandingEnabled // is it enabled at all? - && mExpandedHeight > getFullHeight() // are we past the end? + && mExpandedHeight > getMaxPanelHeight() // are we past the end? && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture? if (mRubberbanding) { mClosing = true; } else if (mVel == 0) { // if the panel is less than halfway open, close it - mClosing = (mFinalTouchY / getFullHeight()) < 0.5f; + mClosing = (mFinalTouchY / getMaxPanelHeight()) < 0.5f; } else { mClosing = mExpandedHeight > 0 && mVel < 0; } @@ -268,7 +269,7 @@ public class PanelView extends FrameLayout { if (DEBUG) logf("tick: v=%.2fpx/s dt=%.4fs", mVel, dt); if (DEBUG) logf("tick: before: h=%d", (int) mExpandedHeight); - final float fh = getFullHeight(); + final float fh = getMaxPanelHeight(); boolean braking = false; if (BRAKES) { if (mClosing) { @@ -351,6 +352,9 @@ public class PanelView extends FrameLayout { mPeekHeight = res.getDimension(R.dimen.peek_height) + getPaddingBottom() // our window might have a dropshadow - (mHandleView == null ? 0 : mHandleView.getPaddingTop()); // the handle might have a topshadow + + final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + mTouchSlop = configuration.getScaledTouchSlop(); } private void trackMovement(MotionEvent event) { @@ -363,10 +367,221 @@ public class PanelView extends FrameLayout { event.offsetLocation(-deltaX, -deltaY); } - // Pass all touches along to the handle, allowing the user to drag the panel closed from its interior @Override public boolean onTouchEvent(MotionEvent event) { - return mHandleView.dispatchTouchEvent(event); + + /* + * We capture touch events here and update the expand height here in case according to + * the users fingers. This also handles multi-touch. + * + * If the user just clicks shortly, we give him a quick peek of the shade. + * + * Flinging is also enabled in order to open or close the shade. + */ + + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float y = event.getY(pointerIndex); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mTracking = true; + if (mHandleView != null) { + mHandleView.setPressed(true); + postInvalidate(); // catch the press state change + } + + mInitialTouchY = y; + initVelocityTracker(); + trackMovement(event); + mTimeAnimator.cancel(); // end any outstanding animations + mBar.onTrackingStarted(PanelView.this); + mInitialOffsetOnTouch = mExpandedHeight; + if (mExpandedHeight == 0) { + mJustPeeked = true; + runPeekAnimation(); + } + break; + + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + final float newY = event.getY(newIndex); + mTrackingPointer = event.getPointerId(newIndex); + mInitialOffsetOnTouch = mExpandedHeight; + mInitialTouchY = newY; + } + break; + + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY + mInitialOffsetOnTouch; + if (h > mPeekHeight) { + if (mPeekAnimator != null && mPeekAnimator.isStarted()) { + mPeekAnimator.cancel(); + } + mJustPeeked = false; + } + if (!mJustPeeked) { + setExpandedHeightInternal(h); + mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); + } + + trackMovement(event); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mFinalTouchY = y; + mTracking = false; + mTrackingPointer = -1; + if (mHandleView != null) { + mHandleView.setPressed(false); + postInvalidate(); // catch the press state change + } + mBar.onTrackingStopped(PanelView.this); + trackMovement(event); + + float vel = getCurrentVelocity(); + fling(vel, true); + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + break; + } + return true; + } + + private float getCurrentVelocity() { + float vel = 0; + float yVel = 0, xVel = 0; + boolean negative = false; + + // the velocitytracker might be null if we got a bad input stream + if (mVelocityTracker == null) { + return 0; + } + + mVelocityTracker.computeCurrentVelocity(1000); + + yVel = mVelocityTracker.getYVelocity(); + negative = yVel < 0; + + xVel = mVelocityTracker.getXVelocity(); + if (xVel < 0) { + xVel = -xVel; + } + if (xVel > mFlingGestureMaxXVelocityPx) { + xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis + } + + vel = (float) Math.hypot(yVel, xVel); + if (vel > mFlingGestureMaxOutputVelocityPx) { + vel = mFlingGestureMaxOutputVelocityPx; + } + + // if you've barely moved your finger, we treat the velocity as 0 + // preventing spurious flings due to touch screen jitter + final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY); + if (deltaY < mFlingGestureMinDistPx + || vel < mFlingExpandMinVelocityPx + ) { + vel = 0; + } + + if (negative) { + vel = -vel; + } + + if (DEBUG) { + logf("gesture: dy=%f vel=(%f,%f) vlinear=%f", + deltaY, + xVel, yVel, + vel); + } + return vel; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + + /* + * If the user drags anywhere inside the panel we intercept it if he moves his finger + * upwards. This allows closing the shade from anywhere inside the panel. + * + * We only do this if the current content is scrolled to the bottom, + * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture + * possible. + */ + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float y = event.getY(pointerIndex); + boolean scrolledToBottom = isScrolledToBottom(); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mTracking = true; + if (mHandleView != null) { + mHandleView.setPressed(true); + // catch the press state change + postInvalidate(); + } + mInitialTouchY = y; + initVelocityTracker(); + trackMovement(event); + mTimeAnimator.cancel(); // end any outstanding animations + if (mExpandedHeight == 0 || y > getContentHeight()) { + return true; + } + break; + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + mTrackingPointer = event.getPointerId(newIndex); + final float newY = event.getY(newIndex); + mInitialTouchY = newY; + } + break; + + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY; + trackMovement(event); + if (scrolledToBottom) { + if (h < -mTouchSlop) { + mInitialOffsetOnTouch = mExpandedHeight; + mInitialTouchY = y; + return true; + } + } + break; + } + return false; + } + + private void initVelocityTracker() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + } + mVelocityTracker = FlingTracker.obtain(); + } + + protected boolean isScrolledToBottom() { + return false; + } + + protected float getContentHeight() { + return mExpandedHeight; } @Override @@ -375,134 +590,6 @@ public class PanelView extends FrameLayout { mHandleView = findViewById(R.id.handle); loadDimens(); - - if (DEBUG) logf("handle view: " + mHandleView); - if (mHandleView != null) { - mHandleView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - int pointerIndex = event.findPointerIndex(mTrackingPointer); - if (pointerIndex < 0) { - pointerIndex = 0; - mTrackingPointer = event.getPointerId(pointerIndex); - } - final float y = event.getY(pointerIndex); - final float rawDelta = event.getRawY() - event.getY(); - final float rawY = y + rawDelta; - if (DEBUG) logf("handle.onTouch: a=%s p=[%d,%d] y=%.1f rawY=%.1f off=%.1f", - MotionEvent.actionToString(event.getAction()), - mTrackingPointer, pointerIndex, - y, rawY, mTouchOffset); - PanelView.this.getLocationOnScreen(mAbsPos); - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mTracking = true; - mHandleView.setPressed(true); - postInvalidate(); // catch the press state change - mInitialTouchY = y; - mVelocityTracker = FlingTracker.obtain(); - trackMovement(event); - mTimeAnimator.cancel(); // end any outstanding animations - mBar.onTrackingStarted(PanelView.this); - mTouchOffset = (rawY - mAbsPos[1]) - mExpandedHeight; - if (mExpandedHeight == 0) { - mJustPeeked = true; - runPeekAnimation(); - } - break; - - case MotionEvent.ACTION_POINTER_UP: - final int upPointer = event.getPointerId(event.getActionIndex()); - if (mTrackingPointer == upPointer) { - // gesture is ongoing, find a new pointer to track - final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; - final float newY = event.getY(newIndex); - final float newRawY = newY + rawDelta; - mTrackingPointer = event.getPointerId(newIndex); - mTouchOffset = (newRawY - mAbsPos[1]) - mExpandedHeight; - mInitialTouchY = newY; - } - break; - - case MotionEvent.ACTION_MOVE: - final float h = rawY - mAbsPos[1] - mTouchOffset; - if (h > mPeekHeight) { - if (mPeekAnimator != null && mPeekAnimator.isStarted()) { - mPeekAnimator.cancel(); - } - mJustPeeked = false; - } - if (!mJustPeeked) { - PanelView.this.setExpandedHeightInternal(h); - mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); - } - - trackMovement(event); - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mFinalTouchY = y; - mTracking = false; - mTrackingPointer = -1; - mHandleView.setPressed(false); - postInvalidate(); // catch the press state change - mBar.onTrackingStopped(PanelView.this); - trackMovement(event); - - float vel = 0, yVel = 0, xVel = 0; - boolean negative = false; - - if (mVelocityTracker != null) { - // the velocitytracker might be null if we got a bad input stream - mVelocityTracker.computeCurrentVelocity(1000); - - yVel = mVelocityTracker.getYVelocity(); - negative = yVel < 0; - - xVel = mVelocityTracker.getXVelocity(); - if (xVel < 0) { - xVel = -xVel; - } - if (xVel > mFlingGestureMaxXVelocityPx) { - xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis - } - - vel = (float)Math.hypot(yVel, xVel); - if (vel > mFlingGestureMaxOutputVelocityPx) { - vel = mFlingGestureMaxOutputVelocityPx; - } - - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - - // if you've barely moved your finger, we treat the velocity as 0 - // preventing spurious flings due to touch screen jitter - final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY); - if (deltaY < mFlingGestureMinDistPx - || vel < mFlingExpandMinVelocityPx - ) { - vel = 0; - } - - if (negative) { - vel = -vel; - } - - if (DEBUG) logf("gesture: dy=%f vel=(%f,%f) vlinear=%f", - deltaY, - xVel, yVel, - vel); - - fling(vel, true); - - break; - } - return true; - }}); - } } public void fling(float vel, boolean always) { @@ -543,19 +630,18 @@ public class PanelView extends FrameLayout { // Did one of our children change size? int newHeight = getMeasuredHeight(); - if (newHeight != mFullHeight) { - mFullHeight = newHeight; - // If the user isn't actively poking us, let's rubberband to the content - if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted() - && mExpandedHeight > 0 && mExpandedHeight != mFullHeight) { - mExpandedHeight = mFullHeight; - } + if (newHeight != mMaxPanelHeight) { + mMaxPanelHeight = newHeight; } heightMeasureSpec = MeasureSpec.makeMeasureSpec( - (int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec)); + getDesiredMeasureHeight(), MeasureSpec.AT_MOST); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } + protected int getDesiredMeasureHeight() { + return (int) mExpandedHeight; + } + public void setExpandedHeight(float height) { if (DEBUG) logf("setExpandedHeight(%.1f)", height); @@ -569,8 +655,20 @@ public class PanelView extends FrameLayout { @Override protected void onLayout (boolean changed, int left, int top, int right, int bottom) { - if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, (int)mExpandedHeight, mFullHeight); + if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, + (int)mExpandedHeight, mMaxPanelHeight); super.onLayout(changed, left, top, right, bottom); + requestPanelHeightUpdate(); + } + + protected void requestPanelHeightUpdate() { + float currentMaxPanelHeight = getMaxPanelHeight(); + + // If the user isn't actively poking us, let's update the height + if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted() + && mExpandedHeight > 0 && currentMaxPanelHeight != mExpandedHeight) { + setExpandedHeightInternal(currentMaxPanelHeight); + } } public void setExpandedHeightInternal(float h) { @@ -583,7 +681,7 @@ public class PanelView extends FrameLayout { h = 0; } - float fh = getFullHeight(); + float fh = getMaxPanelHeight(); if (fh == 0) { // Hmm, full height hasn't been computed yet } @@ -593,9 +691,13 @@ public class PanelView extends FrameLayout { mExpandedHeight = h; - if (DEBUG) logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f"); + if (DEBUG) { + logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, + mTracking ? "T" : "f", mRubberbanding ? "T" : "f"); + } + + onHeightUpdated(mExpandedHeight); - requestLayout(); // FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); // lp.height = (int) mExpandedHeight; // setLayoutParams(lp); @@ -603,13 +705,23 @@ public class PanelView extends FrameLayout { mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh); } - private float getFullHeight() { - if (mFullHeight <= 0) { - if (DEBUG) logf("Forcing measure() since fullHeight=" + mFullHeight); + protected void onHeightUpdated(float expandedHeight) { + requestLayout(); + } + + /** + * This returns the maximum height of the panel. Children should override this if their + * desired height is not the full height. + * + * @return the default implementation simply returns the maximum height. + */ + protected int getMaxPanelHeight() { + if (mMaxPanelHeight <= 0) { + if (DEBUG) logf("Forcing measure() since mMaxPanelHeight=" + mMaxPanelHeight); measure(MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY)); } - return mFullHeight; + return mMaxPanelHeight; } public void setExpandedFraction(float frac) { @@ -621,7 +733,7 @@ public class PanelView extends FrameLayout { } frac = 0; } - setExpandedHeight(getFullHeight() * frac); + setExpandedHeight(getMaxPanelHeight() * frac); } public float getExpandedHeight() { @@ -633,7 +745,7 @@ public class PanelView extends FrameLayout { } public boolean isFullyExpanded() { - return mExpandedHeight >= getFullHeight(); + return mExpandedHeight >= getMaxPanelHeight(); } public boolean isFullyCollapsed() { @@ -681,12 +793,12 @@ public class PanelView extends FrameLayout { } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(String.format("[PanelView(%s): expandedHeight=%f fullHeight=%f closing=%s" + pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%f closing=%s" + " tracking=%s rubberbanding=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s" + "]", this.getClass().getSimpleName(), getExpandedHeight(), - getFullHeight(), + getMaxPanelHeight(), mClosing?"T":"f", mTracking?"T":"f", mRubberbanding?"T":"f", diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 2257aaa..4730f2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -86,7 +86,6 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.DemoMode; import com.android.systemui.EventLogTags; import com.android.systemui.R; -import com.android.systemui.SwipeHelper; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.GestureRecorder; @@ -101,8 +100,6 @@ import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NotificationRowLayout; -import com.android.systemui.statusbar.policy.OnSizeChangedListener; import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -172,7 +169,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { Display mDisplay; Point mCurrentDisplaySize = new Point(); private float mHeadsUpVerticalOffset; - private int[] mPilePosition = new int[2]; + private int[] mStackScrollerPosition = new int[2]; StatusBarWindowView mStatusBarWindow; PhoneStatusBarView mStatusBarView; @@ -198,7 +195,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { // expanded notifications NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window - View mNotificationScroller; View mExpandedContents; int mNotificationPanelGravity; int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx; @@ -350,6 +346,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } }}; + private Runnable mOnFlipRunnable; + + public void setOnFlipRunnable(Runnable onFlipRunnable) { + mOnFlipRunnable = onFlipRunnable; + } + @Override public void setZenMode(int mode) { super.setZenMode(mode); @@ -417,7 +419,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); mStatusBarView.setPanelHolder(holder); - mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel); + mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( + R.id.notification_panel); mNotificationPanel.setStatusBar(this); mNotificationPanelIsFullScreenWidth = (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT); @@ -443,7 +446,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mHeadsUpNotificationView.setBar(this); } if (MULTIUSER_DEBUG) { - mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info); + mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( + R.id.header_debug_info); mNotificationPanelDebugText.setVisibility(View.VISIBLE); } @@ -482,33 +486,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); mTickerView = mStatusBarView.findViewById(R.id.ticker); - View legacyScrollView = mStatusBarWindow.findViewById(R.id.scroll); - NotificationStackScrollLayout notificationStack - = (NotificationStackScrollLayout) mStatusBarWindow - .findViewById(R.id.notification_stack_scroller); - if (ENABLE_NOTIFICATION_STACK) { - notificationStack.setLongPressListener(getNotificationLongClicker()); - mPile = notificationStack; - legacyScrollView.setVisibility(View.GONE); - - // The scrollview and the notification container are unified now! - // TODO: remove mNotificationScroller entirely once we fully switch to the new Layout - mNotificationScroller = notificationStack; - } else { - mNotificationScroller = legacyScrollView; - // less drawing during pulldowns - mNotificationScroller.setVerticalScrollBarEnabled(false); - NotificationRowLayout rowLayout - = (NotificationRowLayout) mStatusBarWindow.findViewById(R.id.latestItems); - rowLayout.setLayoutTransitionsEnabled(false); - rowLayout.setLongPressListener(getNotificationLongClicker()); - mPile = rowLayout; - notificationStack.setVisibility(View.GONE); - } - - mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout); - + mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( + R.id.notification_stack_scroller); + mStackScroller.setLongPressListener(getNotificationLongClicker()); + mExpandedContents = mStackScroller; mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header); @@ -551,7 +533,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } } if (mHasFlipSettings) { - mNotificationButton = (ImageView) mStatusBarWindow.findViewById(R.id.notification_button); + mNotificationButton = (ImageView) mStatusBarWindow.findViewById( + R.id.notification_button); if (mNotificationButton != null) { mNotificationButton.setOnClickListener(mNotificationButtonListener); } @@ -593,17 +576,18 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (isAPhone) { mEmergencyCallLabel = (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only); - if (mEmergencyCallLabel != null) { - mNetworkController.addEmergencyLabelView(mEmergencyCallLabel); - mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { }}); - mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - updateCarrierLabelVisibility(false); - }}); - } + // TODO: Uncomment when correctly positioned +// if (mEmergencyCallLabel != null) { +// mNetworkController.addEmergencyLabelView(mEmergencyCallLabel); +// mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() { +// public void onClick(View v) { }}); +// mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { +// @Override +// public void onLayoutChange(View v, int left, int top, int right, int bottom, +// int oldLeft, int oldTop, int oldRight, int oldBottom) { +// updateCarrierLabelVisibility(false); +// }}); +// } } mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); @@ -621,13 +605,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } // set up the dynamic hide/show of the label - if (!ENABLE_NOTIFICATION_STACK) - ((NotificationRowLayout) mPile).setOnSizeChangedListener(new OnSizeChangedListener() { - @Override - public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { - updateCarrierLabelVisibility(false); - } - }); + // TODO: uncomment, handle this for the Stack scroller aswell +// ((NotificationRowLayout) mStackScroller) +// .setOnSizeChangedListener(new OnSizeChangedListener() { +// @Override +// public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { +// updateCarrierLabelVisibility(false); } // Quick Settings (where available, some restrictions apply) @@ -1066,7 +1049,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } private void loadNotificationShade() { - if (mPile == null) return; + if (mStackScroller == null) return; int N = mNotificationData.size(); @@ -1092,21 +1075,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } ArrayList<View> toRemove = new ArrayList<View>(); - for (int i=0; i<mPile.getChildCount(); i++) { - View child = mPile.getChildAt(i); + for (int i=0; i< mStackScroller.getChildCount(); i++) { + View child = mStackScroller.getChildAt(i); if (!toShow.contains(child)) { toRemove.add(child); } } for (View remove : toRemove) { - mPile.removeView(remove); + mStackScroller.removeView(remove); } for (int i=0; i<toShow.size(); i++) { View v = toShow.get(i); if (v.getParent() == null) { - mPile.addView(v, i); + mStackScroller.addView(v, i); } } @@ -1178,15 +1161,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { // The idea here is to only show the carrier label when there is enough room to see it, // i.e. when there aren't enough notifications to fill the panel. if (SPEW) { - Log.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d", - mPile.getHeight(), mNotificationScroller.getHeight(), mCarrierLabelHeight)); + Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d", + mStackScroller.getHeight(), mStackScroller.getHeight(), + mCarrierLabelHeight)); } final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null; final boolean makeVisible = !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) - && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight) - && mNotificationScroller.getVisibility() == View.VISIBLE; + && mStackScroller.getHeight() < (mNotificationPanel.getHeight() + - mCarrierLabelHeight - mNotificationHeaderHeight) + && mStackScroller.getVisibility() == View.VISIBLE; if (force || mCarrierLabelVisible != makeVisible) { mCarrierLabelVisible = makeVisible; @@ -1229,7 +1214,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (mHasFlipSettings && mFlipSettingsView != null && mFlipSettingsView.getVisibility() == View.VISIBLE - && mNotificationScroller.getVisibility() != View.VISIBLE) { + && mStackScroller.getVisibility() != View.VISIBLE) { // the flip settings panel is unequivocally showing; we should not be shown mClearButton.setVisibility(View.INVISIBLE); } else if (mClearButton.isShown()) { @@ -1483,9 +1468,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } mExpandedVisible = true; - if (!ENABLE_NOTIFICATION_STACK) { - ((NotificationRowLayout) mPile).setLayoutTransitionsEnabled(true); - } if (mNavigationBarView != null) mNavigationBarView.setSlippery(true); @@ -1600,7 +1582,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } mNotificationPanel.expand(); - if (mHasFlipSettings && mNotificationScroller.getVisibility() != View.VISIBLE) { + if (mHasFlipSettings && mStackScroller.getVisibility() != View.VISIBLE) { flipToNotifications(); } @@ -1614,11 +1596,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); if (mClearButtonAnim != null) mClearButtonAnim.cancel(); - mNotificationScroller.setVisibility(View.VISIBLE); + mStackScroller.setVisibility(View.VISIBLE); mScrollViewAnim = start( startDelay(FLIP_DURATION_OUT, interpolator(mDecelerateInterpolator, - ObjectAnimator.ofFloat(mNotificationScroller, View.SCALE_X, 0f, 1f) + ObjectAnimator.ofFloat(mStackScroller, View.SCALE_X, 0f, 1f) .setDuration(FLIP_DURATION_IN) ))); mFlipSettingsViewAnim = start( @@ -1645,6 +1627,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { updateCarrierLabelVisibility(false); } }, FLIP_DURATION - 150); + if (mOnFlipRunnable != null) { + mOnFlipRunnable.run(); + } } @Override @@ -1676,11 +1661,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mFlipSettingsView.setScaleX(1f); mFlipSettingsView.setVisibility(View.VISIBLE); mSettingsButton.setVisibility(View.GONE); - mNotificationScroller.setVisibility(View.GONE); - mNotificationScroller.setScaleX(0f); + mStackScroller.setVisibility(View.GONE); + mStackScroller.setScaleX(0f); mNotificationButton.setVisibility(View.VISIBLE); mNotificationButton.setAlpha(1f); mClearButton.setVisibility(View.GONE); + if (mOnFlipRunnable != null) { + mOnFlipRunnable.run(); + } + } + + public boolean isFlippedToSettings() { + if (mFlipSettingsView != null) { + return mFlipSettingsView.getVisibility() == View.VISIBLE; + } + return false; } public void flipToSettings() { @@ -1704,15 +1699,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mScrollViewAnim = start( setVisibilityWhenDone( interpolator(mAccelerateInterpolator, - ObjectAnimator.ofFloat(mNotificationScroller, View.SCALE_X, 1f, 0f) + ObjectAnimator.ofFloat(mStackScroller, View.SCALE_X, 1f, 0f) ) .setDuration(FLIP_DURATION_OUT), - mNotificationScroller, View.INVISIBLE)); + mStackScroller, View.INVISIBLE)); mSettingsButtonAnim = start( setVisibilityWhenDone( ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f) .setDuration(FLIP_DURATION), - mNotificationScroller, View.INVISIBLE)); + mStackScroller, View.INVISIBLE)); mNotificationButton.setVisibility(View.VISIBLE); mNotificationButtonAnim = start( ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f) @@ -1727,6 +1722,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { updateCarrierLabelVisibility(false); } }, FLIP_DURATION - 150); + if (mOnFlipRunnable != null) { + mOnFlipRunnable.run(); + } } public void flipPanels() { @@ -1766,8 +1764,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); if (mClearButtonAnim != null) mClearButtonAnim.cancel(); - mNotificationScroller.setScaleX(1f); - mNotificationScroller.setVisibility(View.VISIBLE); + mStackScroller.setScaleX(1f); + mStackScroller.setVisibility(View.VISIBLE); mSettingsButton.setAlpha(1f); mSettingsButton.setVisibility(View.VISIBLE); mNotificationPanel.setVisibility(View.GONE); @@ -1777,9 +1775,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } mExpandedVisible = false; - if (!ENABLE_NOTIFICATION_STACK) { - ((NotificationRowLayout) mPile).setLayoutTransitionsEnabled(false); - } if (mNavigationBarView != null) mNavigationBarView.setSlippery(false); visibilityChanged(false); @@ -1806,53 +1801,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); } - /** - * Enables or disables layers on the children of the notifications pile. - * - * When layers are enabled, this method attempts to enable layers for the minimal - * number of children. Only children visible when the notification area is fully - * expanded will receive a layer. The technique used in this method might cause - * more children than necessary to get a layer (at most one extra child with the - * current UI.) - * - * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE} - */ - private void setPileLayers(int layerType) { - final int count = mPile.getChildCount(); - - switch (layerType) { - case View.LAYER_TYPE_NONE: - for (int i = 0; i < count; i++) { - mPile.getChildAt(i).setLayerType(layerType, null); - } - break; - case View.LAYER_TYPE_HARDWARE: - final int[] location = new int[2]; - mNotificationPanel.getLocationInWindow(location); - - final int left = location[0]; - final int top = location[1]; - final int right = left + mNotificationPanel.getWidth(); - final int bottom = top + getExpandedViewMaxHeight(); - - final Rect childBounds = new Rect(); - - for (int i = 0; i < count; i++) { - final View view = mPile.getChildAt(i); - view.getLocationInWindow(location); - - childBounds.set(location[0], location[1], - location[0] + view.getWidth(), location[1] + view.getHeight()); - - if (childBounds.intersects(left, top, right, bottom)) { - view.setLayerType(layerType, null); - } - } - - break; - } - } - public boolean interceptTouchEvent(MotionEvent event) { if (DEBUG_GESTURES) { if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { @@ -2230,11 +2178,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { pw.println(" mTicking=" + mTicking); pw.println(" mTracking=" + mTracking); pw.println(" mDisplayMetrics=" + mDisplayMetrics); - pw.println(" mPile: " + viewInfo(mPile)); + pw.println(" mStackScroller: " + viewInfo(mStackScroller)); pw.println(" mTickerView: " + viewInfo(mTickerView)); - pw.println(" mNotificationScroller: " + viewInfo(mNotificationScroller) - + " scroll " + mNotificationScroller.getScrollX() - + "," + mNotificationScroller.getScrollY()); + pw.println(" mStackScroller: " + viewInfo(mStackScroller) + + " scroll " + mStackScroller.getScrollX() + + "," + mStackScroller.getScrollY()); } pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); @@ -2409,8 +2357,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) { mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx); - mPile.getLocationOnScreen(mPilePosition); - mHeadsUpVerticalOffset = mPilePosition[1] - mNaturalBarHeight; + mStackScroller.getLocationOnScreen(mStackScrollerPosition); + mHeadsUpVerticalOffset = mStackScrollerPosition[1] - mNaturalBarHeight; } updateCarrierLabelVisibility(false); @@ -2428,7 +2376,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { private View.OnClickListener mClearButtonListener = new View.OnClickListener() { public void onClick(View v) { - // TODO: Handle this better with notification stack scroller synchronized (mNotificationData) { mPostCollapseCleanup = new Runnable() { @Override @@ -2437,86 +2384,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { Log.v(TAG, "running post-collapse cleanup"); } try { - if (!ENABLE_NOTIFICATION_STACK) { - ((NotificationRowLayout) mPile).setViewRemoval(true); - } mBarService.onClearAllNotifications(mCurrentUserId); } catch (Exception ex) { } } }; - if(ENABLE_NOTIFICATION_STACK) { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - return; - } - - // animate-swipe all dismissable notifications, then animate the shade closed - int numChildren = mPile.getChildCount(); - - int scrollTop = mNotificationScroller.getScrollY(); - int scrollBottom = scrollTop + mNotificationScroller.getHeight(); - final ArrayList<View> snapshot = new ArrayList<View>(numChildren); - for (int i=0; i<numChildren; i++) { - final View child = mPile.getChildAt(i); - if (((SwipeHelper.Callback) mPile).canChildBeDismissed(child) - && child.getBottom() > scrollTop && child.getTop() < scrollBottom) { - snapshot.add(child); - } - } - if (snapshot.isEmpty()) { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - return; - } - new Thread(new Runnable() { - @Override - public void run() { - // Decrease the delay for every row we animate to give the sense of - // accelerating the swipes - final int ROW_DELAY_DECREMENT = 10; - int currentDelay = 140; - int totalDelay = 0; - - - if (!ENABLE_NOTIFICATION_STACK) { - // Set the shade-animating state to avoid doing other work during - // all of these animations. In particular, avoid layout and - // redrawing when collapsing the shade. - ((NotificationRowLayout) mPile).setViewRemoval(false); - } - - View sampleView = snapshot.get(0); - int width = sampleView.getWidth(); - final int dir = sampleView.isLayoutRtl() ? -1 : +1; - final int velocity = dir * width * 8; // 1000/8 = 125 ms duration - for (final View _v : snapshot) { - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - if (!ENABLE_NOTIFICATION_STACK) { - ((NotificationRowLayout) mPile).dismissRowAnimated( - _v, velocity); - } else { - ((NotificationStackScrollLayout) mPile).dismissRowAnimated( - _v, velocity); - } - } - }, totalDelay); - currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT); - totalDelay += currentDelay; - } - // Delay the collapse animation until after all swipe animations have - // finished. Provide some buffer because there may be some extra delay - // before actually starting each swipe animation. Ideally, we'd - // synchronize the end of those animations with the start of the collaps - // exactly. - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - } - }, totalDelay + 225); - } - }).start(); + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + return; + // TODO: Handle this better with notification stack scroller } } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index eeae081..a7121c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -24,13 +24,13 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.ViewRootImpl; import android.widget.FrameLayout; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; +import com.android.systemui.statusbar.policy.ScrollAdapter; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -40,9 +40,8 @@ public class StatusBarWindowView extends FrameLayout public static final boolean DEBUG = BaseStatusBar.DEBUG; private ExpandHelper mExpandHelper; - private ViewGroup latestItems; + private NotificationStackScrollLayout mStackScrollLayout; private NotificationPanelView mNotificationPanel; - private View mNotificationScroller; PhoneStatusBar mService; @@ -56,37 +55,15 @@ public class StatusBarWindowView extends FrameLayout protected void onAttachedToWindow () { super.onAttachedToWindow(); - ExpandHelper.ScrollAdapter scrollAdapter; - if (BaseStatusBar.ENABLE_NOTIFICATION_STACK) { - NotificationStackScrollLayout stackScrollLayout = - (NotificationStackScrollLayout) findViewById(R.id.notification_stack_scroller); - - // ScrollView and notification container are unified in a single view now. - latestItems = stackScrollLayout; - scrollAdapter = stackScrollLayout; - mNotificationScroller = stackScrollLayout; - } else { - latestItems = (ViewGroup) findViewById(R.id.latestItems); - mNotificationScroller = findViewById(R.id.scroll); - scrollAdapter = new ExpandHelper.ScrollAdapter() { - @Override - public boolean isScrolledToTop() { - return mNotificationScroller.getScrollY() == 0; - } - - @Override - public View getHostView() { - return mNotificationScroller; - } - }; - } + mStackScrollLayout = (NotificationStackScrollLayout) findViewById( + R.id.notification_stack_scroller); mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel); int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height); int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height); - mExpandHelper = new ExpandHelper(getContext(), (ExpandHelper.Callback) latestItems, + mExpandHelper = new ExpandHelper(getContext(), mStackScrollLayout, minHeight, maxHeight); mExpandHelper.setEventSource(this); - mExpandHelper.setScrollAdapter(scrollAdapter); + mExpandHelper.setScrollAdapter(mStackScrollLayout); // We really need to be able to animate while window animations are going on // so that activities may be started asynchronously from panel animations @@ -113,7 +90,7 @@ public class StatusBarWindowView extends FrameLayout public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercept = false; if (mNotificationPanel.isFullyExpanded() - && mNotificationScroller.getVisibility() == View.VISIBLE) { + && mStackScrollLayout.getVisibility() == View.VISIBLE) { intercept = mExpandHelper.onInterceptTouchEvent(ev); } if (!intercept) { @@ -122,7 +99,7 @@ public class StatusBarWindowView extends FrameLayout if (intercept) { MotionEvent cancellation = MotionEvent.obtain(ev); cancellation.setAction(MotionEvent.ACTION_CANCEL); - latestItems.onInterceptTouchEvent(cancellation); + mStackScrollLayout.onInterceptTouchEvent(cancellation); cancellation.recycle(); } return intercept; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java new file mode 100644 index 0000000..f35e22d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.policy; + +import android.view.View; + +/** + * A scroll adapter which can be queried for meta information about the scroll state + */ +public interface ScrollAdapter { + + /** + * @return Whether the view returned by {@link #getHostView()} is scrolled to the top + */ + public boolean isScrolledToTop(); + + /** + * @return Whether the view returned by {@link #getHostView()} is scrolled to the bottom + */ + public boolean isScrolledToBottom(); + + /** + * @return The view in which the scrolling is performed + */ + public View getHostView(); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index f6eeb6d..04b7f53 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -39,12 +39,13 @@ import com.android.systemui.R; import com.android.systemui.SwipeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.stack.StackScrollState.ViewState; +import com.android.systemui.statusbar.policy.ScrollAdapter; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. */ public class NotificationStackScrollLayout extends ViewGroup - implements SwipeHelper.Callback, ExpandHelper.Callback, ExpandHelper.ScrollAdapter { + implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter { private static final String TAG = "NotificationStackScrollLayout"; private static final boolean DEBUG = false; @@ -55,7 +56,7 @@ public class NotificationStackScrollLayout extends ViewGroup private static final int INVALID_POINTER = -1; private SwipeHelper mSwipeHelper; - private boolean mAllowScrolling = true; + private boolean mSwipingInProgress = true; private int mCurrentStackHeight = Integer.MAX_VALUE; private int mOwnScrollY; private int mMaxLayoutHeight; @@ -89,7 +90,7 @@ public class NotificationStackScrollLayout extends ViewGroup * The current State this Layout is in */ private final StackScrollState mCurrentStackScrollState = new StackScrollState(this); - + private OnChildLocationsChangedListener mListener; public NotificationStackScrollLayout(Context context) { @@ -279,7 +280,7 @@ public class NotificationStackScrollLayout extends ViewGroup } /** - * Get the current height of the view. This is at most the size of the view given by a the + * Get the current height of the view. This is at most the msize of the view given by a the * layout but it can also be made smaller by setting {@link #mCurrentStackHeight} * * @return either the layout height or the externally defined height, whichever is smaller @@ -288,6 +289,14 @@ public class NotificationStackScrollLayout extends ViewGroup return Math.min(mMaxLayoutHeight, mCurrentStackHeight); } + public int getItemHeight() { + return mCollapsedSize; + } + + public int getBottomStackPeekSize() { + return mBottomStackPeekSize; + } + public void setLongPressListener(View.OnLongClickListener listener) { mSwipeHelper.setLongPressListener(listener); } @@ -298,15 +307,15 @@ public class NotificationStackScrollLayout extends ViewGroup if (veto != null && veto.getVisibility() != View.GONE) { veto.performClick(); } - allowScrolling(true); + setSwipingInProgress(false); } public void onBeginDrag(View v) { - allowScrolling(false); + setSwipingInProgress(true); } public void onDragCancelled(View v) { - allowScrolling(true); + setSwipingInProgress(false); } public View getChildAtPosition(MotionEvent ev) { @@ -365,8 +374,11 @@ public class NotificationStackScrollLayout extends ViewGroup return (veto != null && veto.getVisibility() != View.GONE); } - private void allowScrolling(boolean allow) { - mAllowScrolling = allow; + private void setSwipingInProgress(boolean isSwiped) { + mSwipingInProgress = isSwiped; + if(isSwiped) { + requestDisallowInterceptTouchEvent(true); + } } @Override @@ -386,7 +398,7 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean onTouchEvent(MotionEvent ev) { boolean scrollerWantsIt = false; - if (mAllowScrolling) { + if (!mSwipingInProgress) { scrollerWantsIt = onScrollTouch(ev); } boolean horizontalSwipeWantsIt = false; @@ -409,12 +421,6 @@ public class NotificationStackScrollLayout extends ViewGroup } boolean isBeingDragged = !mScroller.isFinished(); setIsBeingDragged(isBeingDragged); - if (isBeingDragged) { - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } - } /* * If being flinged and user touches, stop the fling. isFinished @@ -439,10 +445,6 @@ public class NotificationStackScrollLayout extends ViewGroup final int y = (int) ev.getY(activePointerIndex); int deltaY = mLastMotionY - y; if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) { - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } setIsBeingDragged(true); if (deltaY > 0) { deltaY -= mTouchSlop; @@ -642,7 +644,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (getChildCount() > 0) { int contentHeight = getContentHeight(); scrollRange = Math.max(0, - contentHeight - mMaxLayoutHeight + mCollapsedSize); + contentHeight - mMaxLayoutHeight + mBottomStackPeekSize); } return scrollRange; } @@ -697,7 +699,7 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean scrollWantsIt = false; - if (mAllowScrolling) { + if (!mSwipingInProgress) { scrollWantsIt = onInterceptTouchEventScroll(ev); } boolean swipeWantsIt = false; @@ -763,10 +765,6 @@ public class NotificationStackScrollLayout extends ViewGroup mLastMotionY = y; initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } } break; } @@ -823,6 +821,7 @@ public class NotificationStackScrollLayout extends ViewGroup private void setIsBeingDragged(boolean isDragged) { mIsBeingDragged = isDragged; if (isDragged) { + requestDisallowInterceptTouchEvent(true); mSwipeHelper.removeLongPressCallback(); } } @@ -841,10 +840,19 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override + public boolean isScrolledToBottom() { + return mOwnScrollY >= getScrollRange(); + } + + @Override public View getHostView() { return this; } + public int getEmptyBottomMargin() { + return Math.max(getHeight() - mContentHeight, 0); + } + /** * A listener that is notified when some child locations might have changed. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 6d2ba6a..4745f3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -86,7 +86,7 @@ public class StackScrollAlgorithm { // First we reset the view states to their default values. resultState.resetViewStates(); - // The first element is always in there so it's initialized with 1.0f. + // The first element is always in there so it's initialized with 1.0f; algorithmState.itemsInTopStack = 1.0f; algorithmState.partialInTop = 0.0f; algorithmState.lastTopStackIndex = 0; @@ -102,7 +102,7 @@ public class StackScrollAlgorithm { // Phase 3: updateZValuesForState(resultState, algorithmState); - // Write the algorithm state to the result. + // write the algorithm state to the result resultState.setScrollY(algorithmState.scrollY); } @@ -151,7 +151,7 @@ public class StackScrollAlgorithm { // Case 2: // First element of regular scrollview comes next, so the position is just the // scrolling position - nextYPosition = scrollOffset; + nextYPosition = Math.min(scrollOffset, transitioningPositionStart); childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING; } else if (nextYPosition >= transitioningPositionStart) { if (currentYPosition >= transitioningPositionStart) { @@ -180,6 +180,7 @@ public class StackScrollAlgorithm { if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) { Log.wtf(LOG_TAG, "Failed to assign location for child " + i); } + nextYPosition = Math.max(0, nextYPosition); currentYPosition = nextYPosition; yPositionInScrollView = yPositionInScrollViewAfterElement; } @@ -253,6 +254,8 @@ public class StackScrollAlgorithm { nextYPosition = mCollapsedSize + mPaddingBetweenElements - mTopStackIndentationFunctor.getValue( algorithmState.itemsInTopStack - i - 1); + nextYPosition = Math.min(nextYPosition, mLayoutHeight - mCollapsedSize + - mBottomStackPeekSize); if (paddedIndex == 0) { childViewState.alpha = 1.0f - algorithmState.partialInTop; childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN; |