diff options
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; |