From 40c6ae4d22967379ba4793ac8060b38ddfcf84c2 Mon Sep 17 00:00:00 2001 From: Daniel Sandler Date: Tue, 11 Sep 2012 15:18:47 -0400 Subject: [PATCH 1/2] Single finger notification expansion. If you liked these changes... Change Ie4e79aa5 Change I8a6f8606 Change I824937e9 Change I957b6d50 You'll love this one! Change-Id: I5256366175fa7ebc965b1c5df02f10ba802ed977 --- .../res/layout/status_bar_expanded.xml | 2 +- packages/SystemUI/res/values/config.xml | 3 + packages/SystemUI/res/values/dimens.xml | 3 + packages/SystemUI/res/values/ids.xml | 1 + .../com/android/systemui/ExpandHelper.java | 272 ++++++++++++++---- .../systemui/statusbar/BaseStatusBar.java | 20 +- .../systemui/statusbar/NotificationData.java | 26 ++ .../statusbar/phone/StatusBarWindowView.java | 1 + .../policy/NotificationRowLayout.java | 7 + 9 files changed, 276 insertions(+), 59 deletions(-) diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 5841978c2cd09..17dbcaca6d0f0 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -67,7 +67,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fadingEdge="none" - android:overScrollMode="always" + android:overScrollMode="ifContentScrolls" > 20 + + + 10 diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 6c40461f10acc..6fc79c59bd5f6 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -150,4 +150,7 @@ 24dp + + + 32dp diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 8ebbc520197dd..4a732008137a4 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -18,4 +18,5 @@ + diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 5dd15c34a313d..674d9a3e0aac4 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -22,24 +22,31 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.os.Vibrator; import android.util.Slog; import android.view.Gravity; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.View.OnClickListener; +import java.util.Stack; + public class ExpandHelper implements Gefingerpoken, OnClickListener { public interface Callback { View getChildAtRawPosition(float x, float y); View getChildAtPosition(float x, float y); boolean canChildBeExpanded(View v); - boolean setUserExpandedChild(View v, boolean userxpanded); + boolean setUserExpandedChild(View v, boolean userExpanded); + boolean setUserLockedChild(View v, boolean userLocked); } private static final String TAG = "ExpandHelper"; protected static final boolean DEBUG = false; + protected static final boolean DEBUG_SCALE = false; + protected static final boolean DEBUG_GLOW = false; private static final long EXPAND_DURATION = 250; private static final long GLOW_DURATION = 150; @@ -63,6 +70,9 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private Context mContext; private boolean mStretching; + private boolean mPullingWithOneFinger; + private boolean mWatchingForPull; + private boolean mHasPopped; private View mEventSource; private View mCurrView; private View mCurrViewTopGlow; @@ -70,7 +80,12 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private float mOldHeight; private float mNaturalHeight; private float mInitialTouchFocusY; + private float mInitialTouchY; private float mInitialTouchSpan; + private int mTouchSlop; + private int mLastMotionY; + private float mPopLimit; + private int mPopDuration; private Callback mCallback; private ScaleGestureDetector mDetector; private ViewScaler mScaler; @@ -78,6 +93,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private AnimatorSet mGlowAnimationSet; private ObjectAnimator mGlowTopAnimation; private ObjectAnimator mGlowBottomAnimation; + private Vibrator mVibrator; private int mSmallSize; private int mLargeSize; @@ -85,6 +101,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private int mGravity; + private View mScrollView; + private class ViewScaler { View mView; @@ -93,7 +111,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mView = v; } public void setHeight(float h) { - if (DEBUG) Slog.v(TAG, "SetHeight: setting to " + h); + if (DEBUG_SCALE) Slog.v(TAG, "SetHeight: setting to " + h); ViewGroup.LayoutParams lp = mView.getLayoutParams(); lp.height = (int)h; mView.setLayoutParams(lp); @@ -108,7 +126,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { } public int getNaturalHeight(int maximum) { ViewGroup.LayoutParams lp = mView.getLayoutParams(); - if (DEBUG) Slog.v(TAG, "Inspecting a child of type: " + mView.getClass().getName()); + if (DEBUG_SCALE) Slog.v(TAG, "Inspecting a child of type: " + + mView.getClass().getName()); int oldHeight = lp.height; lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; mView.setLayoutParams(lp); @@ -142,6 +161,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mGravity = Gravity.TOP; mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f); mScaleAnimation.setDuration(EXPAND_DURATION); + mPopLimit = mContext.getResources().getDimension(R.dimen.one_finger_pop_limit); + mPopDuration = mContext.getResources().getInteger(R.integer.one_finger_pop_duration_ms); AnimatorListenerAdapter glowVisibilityController = new AnimatorListenerAdapter() { @Override @@ -169,38 +190,30 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mGlowAnimationSet.play(mGlowTopAnimation).with(mGlowBottomAnimation); mGlowAnimationSet.setDuration(GLOW_DURATION); + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mTouchSlop = configuration.getScaledTouchSlop(); + mDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { @Override public boolean onScaleBegin(ScaleGestureDetector detector) { - if (DEBUG) Slog.v(TAG, "onscalebegin()"); + if (DEBUG_SCALE) Slog.v(TAG, "onscalebegin()"); float x = detector.getFocusX(); float y = detector.getFocusY(); - View v = null; - if (mEventSource != null) { - int[] location = new int[2]; - mEventSource.getLocationOnScreen(location); - x += (float) location[0]; - y += (float) location[1]; - v = mCallback.getChildAtRawPosition(x, y); - } else { - v = mCallback.getChildAtPosition(x, y); - } - // your fingers have to be somewhat close to the bounds of the view in question mInitialTouchFocusY = detector.getFocusY(); mInitialTouchSpan = Math.abs(detector.getCurrentSpan()); - if (DEBUG) Slog.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")"); + if (DEBUG_SCALE) Slog.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")"); - mStretching = initScale(v); + mStretching = initScale(findView(x, y)); return mStretching; } @Override public boolean onScale(ScaleGestureDetector detector) { - if (DEBUG) Slog.v(TAG, "onscale() on " + mCurrView); + if (DEBUG_SCALE) Slog.v(TAG, "onscale() on " + mCurrView); // are we scaling or dragging? float span = Math.abs(detector.getCurrentSpan()) - mInitialTouchSpan; @@ -210,33 +223,71 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; float pull = Math.abs(drag) + Math.abs(span) + 1f; float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; - if (DEBUG) Slog.d(TAG, "current span handle is: " + hand); - hand = hand + mOldHeight; - float target = hand; - if (DEBUG) Slog.d(TAG, "target is: " + target); - hand = hand < mSmallSize ? mSmallSize : (hand > mLargeSize ? mLargeSize : hand); - hand = hand > mNaturalHeight ? mNaturalHeight : hand; - if (DEBUG) Slog.d(TAG, "scale continues: hand =" + hand); - mScaler.setHeight(hand); + float target = hand + mOldHeight; + float newHeight = clamp(target); + mScaler.setHeight(newHeight); - // glow if overscale - float stretch = (float) Math.abs((target - hand) / mMaximumStretch); - float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f))); - if (DEBUG) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength); - setGlow(GLOW_BASE + strength * (1f - GLOW_BASE)); + setGlow(calculateGlow(target, newHeight)); return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { - if (DEBUG) Slog.v(TAG, "onscaleend()"); + if (DEBUG_SCALE) Slog.v(TAG, "onscaleend()"); // I guess we're alone now - if (DEBUG) Slog.d(TAG, "scale end"); + if (DEBUG_SCALE) Slog.d(TAG, "scale end"); finishScale(false); + clearView(); + mStretching = false; } }); } + private float clamp(float target) { + float out = target; + out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out); + out = out > mNaturalHeight ? mNaturalHeight : out; + return out; + } + + private View findView(float x, float y) { + View v = null; + if (mEventSource != null) { + int[] location = new int[2]; + mEventSource.getLocationOnScreen(location); + x += (float) location[0]; + y += (float) location[1]; + v = mCallback.getChildAtRawPosition(x, y); + } else { + v = mCallback.getChildAtPosition(x, y); + } + return v; + } + + private boolean isInside(View v, float x, float y) { + if (DEBUG) Slog.d(TAG, "isinside (" + x + ", " + y + ")"); + + if (v == null) { + if (DEBUG) Slog.d(TAG, "isinside null subject"); + return false; + } + if (mEventSource != null) { + int[] location = new int[2]; + mEventSource.getLocationOnScreen(location); + x += (float) location[0]; + y += (float) location[1]; + if (DEBUG) Slog.d(TAG, " to global (" + x + ", " + y + ")"); + } + int[] location = new int[2]; + v.getLocationOnScreen(location); + x -= (float) location[0]; + y -= (float) location[1]; + if (DEBUG) Slog.d(TAG, " to local (" + x + ", " + y + ")"); + if (DEBUG) Slog.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")"); + boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight()); + return inside; + } + public void setEventSource(View eventSource) { mEventSource = eventSource; } @@ -245,13 +296,26 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mGravity = gravity; } + public void setScrollView(View scrollView) { + mScrollView = scrollView; + } + + private float calculateGlow(float target, float actual) { + // glow if overscale + if (DEBUG_GLOW) Slog.d(TAG, "target: " + target + " actual: " + actual); + float stretch = (float) Math.abs((target - actual) / mMaximumStretch); + float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f))); + if (DEBUG_GLOW) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength); + return (GLOW_BASE + strength * (1f - GLOW_BASE)); + } + public void setGlow(float glow) { if (!mGlowAnimationSet.isRunning() || glow == 0f) { if (mGlowAnimationSet.isRunning()) { - mGlowAnimationSet.cancel(); + mGlowAnimationSet.end(); } if (mCurrViewTopGlow != null && mCurrViewBottomGlow != null) { - if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) { + if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) { // animate glow in and out mGlowTopAnimation.setTarget(mCurrViewTopGlow); mGlowBottomAnimation.setTarget(mCurrViewBottomGlow); @@ -278,23 +342,115 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { public boolean onInterceptTouchEvent(MotionEvent ev) { if (DEBUG) Slog.d(TAG, "interceptTouch: act=" + (ev.getAction()) + - " stretching=" + mStretching); + " stretching=" + mStretching + + " onefinger=" + mPullingWithOneFinger); + // check for a two-finger gesture mDetector.onTouchEvent(ev); - return mStretching; + if (mStretching) { + return true; + } else { + final int action = ev.getAction(); + if ((action == MotionEvent.ACTION_MOVE) && mPullingWithOneFinger) { + return true; + } + if (mScrollView != null && mScrollView.getScrollY() > 0) { + return false; + } + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_MOVE: { + if (mWatchingForPull) { + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + final int yDiff = y - mLastMotionY; + if (yDiff > mTouchSlop) { + mLastMotionY = y; + mPullingWithOneFinger = initScale(findView(x, y)); + if (mPullingWithOneFinger) { + mInitialTouchY = mLastMotionY; + mHasPopped = false; + } + } + } + break; + } + + case MotionEvent.ACTION_DOWN: + mWatchingForPull = isInside(mScrollView, ev.getX(), ev.getY()); + mLastMotionY = (int) ev.getY(); + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (mPullingWithOneFinger) { + finishScale(false); + clearView(); + } + mPullingWithOneFinger = false; + mWatchingForPull = false; + break; + } + return mPullingWithOneFinger; + } } public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); - if (DEBUG) Slog.d(TAG, "touch: act=" + (action) + " stretching=" + mStretching); + if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + (action) + + " stretching=" + mStretching + + " onefinger=" + mPullingWithOneFinger); if (mStretching) { - if (DEBUG) Slog.d(TAG, "detector ontouch"); mDetector.onTouchEvent(ev); } switch (action) { + case MotionEvent.ACTION_MOVE: { + if (mPullingWithOneFinger) { + final float rawHeight = ev.getY() - mInitialTouchY + mOldHeight; + final float newHeight = clamp(rawHeight); + final boolean wasClosed = (mOldHeight == mSmallSize); + boolean isFinished = false; + if (rawHeight > mNaturalHeight) { + isFinished = true; + } + if (rawHeight < mSmallSize) { + isFinished = true; + } + + final float pull = Math.abs(ev.getY() - mInitialTouchY); + if (mHasPopped || pull > mPopLimit) { + if (!mHasPopped) { + vibrate(mPopDuration); + mHasPopped = true; + } + } + + if (mHasPopped) { + mScaler.setHeight(newHeight); + setGlow(GLOW_BASE); + } else { + setGlow(calculateGlow(4f * pull, 0f)); + } + + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + View underPointer = findView(x, y); + if (isFinished && underPointer != null && underPointer != mCurrView) { + finishScale(false); + initScale(underPointer); + mInitialTouchY = ev.getY(); + mHasPopped = false; + } + return true; + } + break; + } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (DEBUG) Slog.d(TAG, "cancel"); mStretching = false; + if (mPullingWithOneFinger) { + finishScale(false); + mPullingWithOneFinger = false; + } clearView(); break; } @@ -303,7 +459,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private boolean initScale(View v) { if (v != null) { if (DEBUG) Slog.d(TAG, "scale begins on view: " + v); - mStretching = true; + mCallback.setUserLockedChild(v, true); setView(v); setGlow(GLOW_BASE); mScaler.setView(v); @@ -318,30 +474,34 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight + " mNaturalHeight: " + mNaturalHeight); v.getParent().requestDisallowInterceptTouchEvent(true); + return true; + } else { + return false; } - return mStretching; } private void finishScale(boolean force) { + float currentHeight = mScaler.getHeight(); + float targetHeight = mSmallSize; float h = mScaler.getHeight(); final boolean wasClosed = (mOldHeight == mSmallSize); if (wasClosed) { - h = (force || h > mSmallSize) ? mNaturalHeight : mSmallSize; + targetHeight = (force || currentHeight > mSmallSize) ? mNaturalHeight : mSmallSize; } else { - h = (force || h < mNaturalHeight) ? mSmallSize : mNaturalHeight; + targetHeight = (force || currentHeight < mNaturalHeight) ? mSmallSize : mNaturalHeight; } - if (DEBUG && mCurrView != null) mCurrView.setBackgroundColor(0); if (mScaleAnimation.isRunning()) { mScaleAnimation.cancel(); } - mScaleAnimation.setFloatValues(h); - mScaleAnimation.setupStartValues(); - mScaleAnimation.start(); - mStretching = false; setGlow(0f); mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight); + if (targetHeight != currentHeight) { + mScaleAnimation.setFloatValues(targetHeight); + mScaleAnimation.setupStartValues(); + mScaleAnimation.start(); + } + mCallback.setUserLockedChild(mCurrView, false); if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView); - clearView(); } private void clearView() { @@ -357,7 +517,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mCurrViewTopGlow = g.findViewById(R.id.top_glow); mCurrViewBottomGlow = g.findViewById(R.id.bottom_glow); if (DEBUG) { - String debugLog = "Looking for glows: " + + String debugLog = "Looking for glows: " + (mCurrViewTopGlow != null ? "found top " : "didn't find top") + (mCurrViewBottomGlow != null ? "found bottom " : "didn't find bottom"); Slog.v(TAG, debugLog); @@ -369,6 +529,18 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { public void onClick(View v) { initScale(v); finishScale(true); + clearView(); + } + /** + * Triggers haptic feedback. + */ + private synchronized void vibrate(long duration) { + if (mVibrator == null) { + mVibrator = (android.os.Vibrator) + mContext.getSystemService(Context.VIBRATOR_SERVICE); + } + mVibrator.vibrate(duration); } } + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 1204a8917d05a..8d7734e0dcfa7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -783,16 +783,20 @@ public abstract class BaseStatusBar extends SystemUI implements int N = mNotificationData.size(); for (int i = 0; i < N; i++) { NotificationData.Entry entry = mNotificationData.get(i); - if (i == (N-1)) { - if (DEBUG) Slog.d(TAG, "expanding top notification at " + i); - expandView(entry, true); - } else { - if (!entry.userExpanded()) { - if (DEBUG) Slog.d(TAG, "collapsing notification at " + i); - expandView(entry, false); + if (!entry.userLocked()) { + if (i == (N-1)) { + if (DEBUG) Slog.d(TAG, "expanding top notification at " + i); + expandView(entry, true); } else { - if (DEBUG) Slog.d(TAG, "ignoring user-modified notification at " + i); + if (!entry.userExpanded()) { + if (DEBUG) Slog.d(TAG, "collapsing notification at " + i); + expandView(entry, false); + } else { + if (DEBUG) Slog.d(TAG, "ignoring user-modified notification at " + i); + } } + } else { + if (DEBUG) Slog.d(TAG, "ignoring notification being held by user at " + i); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index dfd8cf8a46890..c82f25008f2d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -71,6 +71,18 @@ public class NotificationData { public boolean setUserExpanded(boolean userExpanded) { return NotificationData.setUserExpanded(row, userExpanded); } + /** + * Return whether the entry is being touched by the user. + */ + public boolean userLocked() { + return NotificationData.getUserLocked(row); + } + /** + * Set the flag indicating that this is being touched by the user. + */ + public boolean setUserLocked(boolean userLocked) { + return NotificationData.setUserLocked(row, userLocked); + } } private final ArrayList mEntries = new ArrayList(); private final Comparator mEntryCmp = new Comparator() { @@ -197,4 +209,18 @@ public class NotificationData { public static boolean setUserExpanded(View row, boolean userExpanded) { return writeBooleanTag(row, R.id.user_expanded_tag, userExpanded); } + + /** + * Return whether the entry is being touched by the user. + */ + public static boolean getUserLocked(View row) { + return readBooleanTag(row, R.id.user_lock_tag); + } + + /** + * Set whether the entry is being touched by the user. + */ + public static boolean setUserLocked(View row, boolean userLocked) { + return writeBooleanTag(row, R.id.user_lock_tag, userLocked); + } } 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 931756197c230..2628631338fa4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -53,6 +53,7 @@ public class StatusBarWindowView extends FrameLayout int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height); mExpandHelper = new ExpandHelper(mContext, latestItems, minHeight, maxHeight); mExpandHelper.setEventSource(this); + mExpandHelper.setScrollView(scroller); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java index 61e5ab63d6e80..89eed1bd6138f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java @@ -78,6 +78,7 @@ public class NotificationRowLayout super(context, attrs, defStyle); mRealLayoutTransition = new LayoutTransition(); + mRealLayoutTransition.setAnimateParentHierarchy(true); setLayoutTransitionsEnabled(true); setOrientation(LinearLayout.VERTICAL); @@ -161,7 +162,12 @@ public class NotificationRowLayout return NotificationData.setUserExpanded(v, userExpanded); } + public boolean setUserLockedChild(View v, boolean userLocked) { + return NotificationData.setUserLocked(v, userLocked); + } + public void onChildDismissed(View v) { + if (DEBUG) Slog.v(TAG, "onChildDismissed: " + v + " mRemoveViews=" + mRemoveViews); final View veto = v.findViewById(R.id.veto); if (veto != null && veto.getVisibility() != View.GONE && mRemoveViews) { veto.performClick(); @@ -225,6 +231,7 @@ public class NotificationRowLayout * get removed properly. */ public void setViewRemoval(boolean removeViews) { + if (DEBUG) Slog.v(TAG, "setViewRemoval: " + removeViews); mRemoveViews = removeViews; } From de3debd9f57ba5c3313d7c785094dc81c16304bf Mon Sep 17 00:00:00 2001 From: Daniel Sandler Date: Tue, 11 Sep 2012 16:56:35 -0400 Subject: [PATCH 2/2] Fix notification gestures. Broken by recent changes to ScaleGestureDetector, which we were using incorrectly. Change-Id: Ie5a7981cc14873d34de9041f59b5de442c948a61 --- packages/SystemUI/res/values/config.xml | 2 +- packages/SystemUI/res/values/dimens.xml | 5 +- .../com/android/systemui/ExpandHelper.java | 240 ++++++++++-------- 3 files changed, 144 insertions(+), 103 deletions(-) diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 66add1aacb08e..fd5ef4ecbee47 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -67,6 +67,6 @@ 20 - 10 + 10 diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 6fc79c59bd5f6..c6fd66abbec4a 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -152,5 +152,8 @@ 24dp - 32dp + 32dp + + + 25dp diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 674d9a3e0aac4..dcfd0b346bdf6 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -50,7 +50,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private static final long EXPAND_DURATION = 250; private static final long GLOW_DURATION = 150; - // Set to false to disable focus-based gestures (two-finger pull). + // Set to false to disable focus-based gestures (spread-finger vertical pull). private static final boolean USE_DRAG = true; // Set to false to disable scale-based gestures (both horizontal and vertical). private static final boolean USE_SPAN = true; @@ -69,8 +69,12 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { @SuppressWarnings("unused") private Context mContext; - private boolean mStretching; - private boolean mPullingWithOneFinger; + private boolean mExpanding; + private static final int NONE = 0; + private static final int BLINDS = 1<<0; + private static final int PULL = 1<<1; + private static final int STRETCH = 1<<2; + private int mExpansionStyle = NONE; private boolean mWatchingForPull; private boolean mHasPopped; private View mEventSource; @@ -86,8 +90,9 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private int mLastMotionY; private float mPopLimit; private int mPopDuration; + private float mPullGestureMinXSpan; private Callback mCallback; - private ScaleGestureDetector mDetector; + private ScaleGestureDetector mSGD; private ViewScaler mScaler; private ObjectAnimator mScaleAnimation; private AnimatorSet mGlowAnimationSet; @@ -122,7 +127,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { if (height < 0) { height = mView.getMeasuredHeight(); } - return (float) height; + return height; } public int getNaturalHeight(int maximum) { ViewGroup.LayoutParams lp = mView.getLayoutParams(); @@ -161,8 +166,9 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mGravity = Gravity.TOP; mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f); mScaleAnimation.setDuration(EXPAND_DURATION); - mPopLimit = mContext.getResources().getDimension(R.dimen.one_finger_pop_limit); - mPopDuration = mContext.getResources().getInteger(R.integer.one_finger_pop_duration_ms); + mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold); + mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms); + mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min); AnimatorListenerAdapter glowVisibilityController = new AnimatorListenerAdapter() { @Override @@ -193,41 +199,30 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { final ViewConfiguration configuration = ViewConfiguration.get(mContext); mTouchSlop = configuration.getScaledTouchSlop(); - mDetector = - new ScaleGestureDetector(context, + mSGD = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { @Override public boolean onScaleBegin(ScaleGestureDetector detector) { if (DEBUG_SCALE) Slog.v(TAG, "onscalebegin()"); - float x = detector.getFocusX(); - float y = detector.getFocusY(); + float focusX = detector.getFocusX(); + float focusY = detector.getFocusY(); // your fingers have to be somewhat close to the bounds of the view in question - mInitialTouchFocusY = detector.getFocusY(); + mInitialTouchFocusY = focusY; mInitialTouchSpan = Math.abs(detector.getCurrentSpan()); if (DEBUG_SCALE) Slog.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")"); - mStretching = initScale(findView(x, y)); - return mStretching; + final View underFocus = findView(focusX, focusY); + if (underFocus != null) { + startExpanding(underFocus, STRETCH); + } + return mExpanding; } @Override public boolean onScale(ScaleGestureDetector detector) { if (DEBUG_SCALE) Slog.v(TAG, "onscale() on " + mCurrView); - - // are we scaling or dragging? - float span = Math.abs(detector.getCurrentSpan()) - mInitialTouchSpan; - span *= USE_SPAN ? 1f : 0f; - float drag = detector.getFocusY() - mInitialTouchFocusY; - drag *= USE_DRAG ? 1f : 0f; - drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; - float pull = Math.abs(drag) + Math.abs(span) + 1f; - float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; - float target = hand + mOldHeight; - float newHeight = clamp(target); - mScaler.setHeight(newHeight); - - setGlow(calculateGlow(target, newHeight)); + updateExpansion(); return true; } @@ -236,13 +231,28 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { if (DEBUG_SCALE) Slog.v(TAG, "onscaleend()"); // I guess we're alone now if (DEBUG_SCALE) Slog.d(TAG, "scale end"); - finishScale(false); + finishExpanding(false); clearView(); - mStretching = false; } }); } + private void updateExpansion() { + // are we scaling or dragging? + float span = Math.abs(mSGD.getCurrentSpan()) - mInitialTouchSpan; + span *= USE_SPAN ? 1f : 0f; + float drag = mSGD.getFocusY() - mInitialTouchFocusY; + drag *= USE_DRAG ? 1f : 0f; + drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; + float pull = Math.abs(drag) + Math.abs(span) + 1f; + float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; + float target = hand + mOldHeight; + float newHeight = clamp(target); + mScaler.setHeight(newHeight); + + setGlow(calculateGlow(target, newHeight)); + } + private float clamp(float target) { float out = target; out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out); @@ -255,8 +265,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { if (mEventSource != null) { int[] location = new int[2]; mEventSource.getLocationOnScreen(location); - x += (float) location[0]; - y += (float) location[1]; + x += location[0]; + y += location[1]; v = mCallback.getChildAtRawPosition(x, y); } else { v = mCallback.getChildAtPosition(x, y); @@ -274,14 +284,14 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { if (mEventSource != null) { int[] location = new int[2]; mEventSource.getLocationOnScreen(location); - x += (float) location[0]; - y += (float) location[1]; + x += location[0]; + y += location[1]; if (DEBUG) Slog.d(TAG, " to global (" + x + ", " + y + ")"); } int[] location = new int[2]; v.getLocationOnScreen(location); - x -= (float) location[0]; - y -= (float) location[1]; + x -= location[0]; + y -= location[1]; if (DEBUG) Slog.d(TAG, " to local (" + x + ", " + y + ")"); if (DEBUG) Slog.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")"); boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight()); @@ -303,7 +313,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private float calculateGlow(float target, float actual) { // glow if overscale if (DEBUG_GLOW) Slog.d(TAG, "target: " + target + " actual: " + actual); - float stretch = (float) Math.abs((target - actual) / mMaximumStretch); + float stretch = Math.abs((target - actual) / mMaximumStretch); float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f))); if (DEBUG_GLOW) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength); return (GLOW_BASE + strength * (1f - GLOW_BASE)); @@ -340,32 +350,54 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { View.INVISIBLE : View.VISIBLE); } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (DEBUG) Slog.d(TAG, "interceptTouch: act=" + (ev.getAction()) + - " stretching=" + mStretching + - " onefinger=" + mPullingWithOneFinger); - // check for a two-finger gesture - mDetector.onTouchEvent(ev); - if (mStretching) { + final int action = ev.getAction(); + if (DEBUG_SCALE) Slog.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) + + " expanding=" + mExpanding + + (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + + (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + + (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); + // check for a spread-finger vertical pull gesture + mSGD.onTouchEvent(ev); + final int x = (int) mSGD.getFocusX(); + final int y = (int) mSGD.getFocusY(); + if (mExpanding) { return true; } else { - final int action = ev.getAction(); - if ((action == MotionEvent.ACTION_MOVE) && mPullingWithOneFinger) { + if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) { + // we've begun Venetian blinds style expansion + return true; + } + final float xspan = mSGD.getCurrentSpanX(); + if ((action == MotionEvent.ACTION_MOVE && + xspan > mPullGestureMinXSpan && + xspan > mSGD.getCurrentSpanY())) { + // detect a vertical pulling gesture with fingers somewhat separated + if (DEBUG_SCALE) Slog.v(TAG, "got pull gesture (xspan=" + xspan + "px)"); + + mInitialTouchFocusY = y; + + final View underFocus = findView(x, y); + if (underFocus != null) { + startExpanding(underFocus, PULL); + } return true; } if (mScrollView != null && mScrollView.getScrollY() > 0) { return false; } + // Now look for other gestures switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_MOVE: { if (mWatchingForPull) { - final int x = (int) ev.getX(); - final int y = (int) ev.getY(); final int yDiff = y - mLastMotionY; if (yDiff > mTouchSlop) { + if (DEBUG) Slog.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); mLastMotionY = y; - mPullingWithOneFinger = initScale(findView(x, y)); - if (mPullingWithOneFinger) { + final View underFocus = findView(x, y); + if (underFocus != null) { + startExpanding(underFocus, BLINDS); mInitialTouchY = mLastMotionY; mHasPopped = false; } @@ -375,35 +407,35 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { } case MotionEvent.ACTION_DOWN: - mWatchingForPull = isInside(mScrollView, ev.getX(), ev.getY()); - mLastMotionY = (int) ev.getY(); + mWatchingForPull = isInside(mScrollView, x, y); + mLastMotionY = y; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: - if (mPullingWithOneFinger) { - finishScale(false); - clearView(); - } - mPullingWithOneFinger = false; - mWatchingForPull = false; + if (DEBUG) Slog.d(TAG, "up/cancel"); + finishExpanding(false); + clearView(); break; } - return mPullingWithOneFinger; + return mExpanding; } } + @Override public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); - if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + (action) + - " stretching=" + mStretching + - " onefinger=" + mPullingWithOneFinger); - if (mStretching) { - mDetector.onTouchEvent(ev); - } + if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + MotionEvent.actionToString(action) + + " expanding=" + mExpanding + + (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + + (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + + (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); + + mSGD.onTouchEvent(ev); + switch (action) { case MotionEvent.ACTION_MOVE: { - if (mPullingWithOneFinger) { + if (0 != (mExpansionStyle & BLINDS)) { final float rawHeight = ev.getY() - mInitialTouchY + mOldHeight; final float newHeight = clamp(rawHeight); final boolean wasClosed = (mOldHeight == mSmallSize); @@ -430,57 +462,59 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { setGlow(calculateGlow(4f * pull, 0f)); } - final int x = (int) ev.getX(); - final int y = (int) ev.getY(); - View underPointer = findView(x, y); - if (isFinished && underPointer != null && underPointer != mCurrView) { - finishScale(false); - initScale(underPointer); - mInitialTouchY = ev.getY(); + final int x = (int) mSGD.getFocusX(); + final int y = (int) mSGD.getFocusY(); + View underFocus = findView(x, y); + if (isFinished && underFocus != null && underFocus != mCurrView) { + finishExpanding(false); // @@@ needed? + startExpanding(underFocus, BLINDS); + mInitialTouchY = y; mHasPopped = false; } return true; } + + if (mExpanding) { + updateExpansion(); + return true; + } + break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (DEBUG) Slog.d(TAG, "cancel"); - mStretching = false; - if (mPullingWithOneFinger) { - finishScale(false); - mPullingWithOneFinger = false; - } + if (DEBUG) Slog.d(TAG, "up/cancel"); + finishExpanding(false); clearView(); break; } return true; } - private boolean initScale(View v) { - if (v != null) { - if (DEBUG) Slog.d(TAG, "scale begins on view: " + v); - mCallback.setUserLockedChild(v, true); - setView(v); - setGlow(GLOW_BASE); - mScaler.setView(v); - mOldHeight = mScaler.getHeight(); - if (mCallback.canChildBeExpanded(v)) { - if (DEBUG) Slog.d(TAG, "working on an expandable child"); - mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); - } else { - if (DEBUG) Slog.d(TAG, "working on a non-expandable child"); - mNaturalHeight = mOldHeight; - } - if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight + - " mNaturalHeight: " + mNaturalHeight); - v.getParent().requestDisallowInterceptTouchEvent(true); - return true; + + private void startExpanding(View v, int expandType) { + mExpanding = true; + mExpansionStyle = expandType; + if (DEBUG) Slog.d(TAG, "scale type " + expandType + " beginning on view: " + v); + mCallback.setUserLockedChild(v, true); + setView(v); + setGlow(GLOW_BASE); + mScaler.setView(v); + mOldHeight = mScaler.getHeight(); + if (mCallback.canChildBeExpanded(v)) { + if (DEBUG) Slog.d(TAG, "working on an expandable child"); + mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); } else { - return false; + if (DEBUG) Slog.d(TAG, "working on a non-expandable child"); + mNaturalHeight = mOldHeight; } + if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight + + " mNaturalHeight: " + mNaturalHeight); + v.getParent().requestDisallowInterceptTouchEvent(true); } - private void finishScale(boolean force) { + private void finishExpanding(boolean force) { + if (!mExpanding) return; + float currentHeight = mScaler.getHeight(); float targetHeight = mSmallSize; float h = mScaler.getHeight(); @@ -501,6 +535,10 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mScaleAnimation.start(); } mCallback.setUserLockedChild(mCurrView, false); + + mExpanding = false; + mExpansionStyle = NONE; + if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView); } @@ -527,8 +565,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { @Override public void onClick(View v) { - initScale(v); - finishScale(true); + startExpanding(v, STRETCH); + finishExpanding(true); clearView(); }