Merge "Enabled the new notification shade and improved expanding logic"

This commit is contained in:
Selim Cinek
2014-04-03 17:58:46 +00:00
committed by Android (Google) Code Review
10 changed files with 539 additions and 530 deletions

View File

@@ -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"

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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.
@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);
}
}
@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);
/**
* 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
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());
}
// TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
// implementation.
return super.onTouchEvent(event);
}
@Override
protected boolean isScrolledToBottom() {
if (!isInSettings()) {
return mNotificationStackScroller.isScrolledToBottom();
}
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.isScrolledToBottom();
}
@Override
protected int getMaxPanelHeight() {
if (!isInSettings()) {
int maxPanelHeight = super.getMaxPanelHeight();
int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
return maxPanelHeight - emptyBottomMargin;
}
return mHandleView.dispatchTouchEvent(event);
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)));
}
}
@Override
protected int getDesiredMeasureHeight() {
return mMaxPanelHeight;
}
}

View File

@@ -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",

View File

@@ -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
}
}
};

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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();
}
}
@@ -840,11 +839,20 @@ public class NotificationStackScrollLayout extends ViewGroup
return mOwnScrollY == 0;
}
@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.
*/

View File

@@ -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;