Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4e2c48b authored by Chris Wren's avatar Chris Wren Committed by Android (Google) Code Review
Browse files

open notifications near the top with a single finger.

Bug: 6538918
Change-Id: I824937e9acd8f20449c31f8d83dbd90ecbb75ef0
parent d80e7fb3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fadingEdge="none"
            android:overScrollMode="ifContentScrolls"
            android:overScrollMode="never"
            android:layout_marginTop="@dimen/notification_panel_header_height"
            >
            <com.android.systemui.statusbar.policy.NotificationRowLayout
+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@
            android:id="@+id/notification_scroller"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:overScrollMode="never"
            android:layout_weight="1"
            >
            <com.android.systemui.statusbar.policy.NotificationRowLayout
+3 −0
Original line number Diff line number Diff line
@@ -65,5 +65,8 @@

    <!-- Vibration duration for MultiWaveView used in SearchPanelView -->
    <integer translatable="false" name="config_search_panel_view_vibration_duration">20</integer>

    <!-- The length of the vibration when the notificaiotn pops open. -->
    <integer name="one_finger_pop_duration_ms">10</integer>
</resources>
+3 −0
Original line number Diff line number Diff line
@@ -150,4 +150,7 @@

    <!-- Height of the carrier/wifi name label -->
    <dimen name="carrier_label_height">24dp</dimen>

    <!-- The distance you can pull a notificaiton before it pops open -->
    <dimen name="one_finger_pop_limit">40dp</dimen>
</resources>
+178 −34
Original line number Diff line number Diff line
@@ -22,11 +22,13 @@ 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;

@@ -63,6 +65,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 +75,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 +88,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 +96,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {

    private int mGravity;

    private View mScrollView;

    private class ViewScaler {
        View mView;

@@ -142,6 +155,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,6 +184,9 @@ 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() {
@@ -178,23 +196,12 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
                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 + ")");

                mStretching = initScale(v);
                mStretching = initScale(findView(x, y));
                return mStretching;
            }

@@ -210,20 +217,11 @@ 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;
            }

@@ -233,10 +231,56 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
                // I guess we're alone now
                if (DEBUG) Slog.d(TAG, "scale end");
                finishScale(false);
                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,6 +289,18 @@ 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
        float stretch = (float) Math.abs((target - actual) / mMaximumStretch);
        float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f)));
        if (DEBUG) 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()) {
@@ -278,23 +334,100 @@ 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) {
                            mInitialTouchFocusY = mLastMotionY;
                            mHasPopped = false;
                        }
                    }
                    if (DEBUG) Slog.d(TAG, "examining move: " + yDiff);
                } else {
                    if (DEBUG) Slog.d(TAG, "uninteresting move");
                }
                break;
            }

            case MotionEvent.ACTION_DOWN:
                mWatchingForPull = isInside(mScrollView, ev.getX(), ev.getY());
                mLastMotionY = (int) ev.getY();
                if (DEBUG) Slog.d(TAG, "action down: " + mWatchingForPull);
                break;

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                if (mPullingWithOneFinger) {
                    finishScale(false);
                }
                mPullingWithOneFinger = false;
                mWatchingForPull = false;
                if (DEBUG) Slog.d(TAG, "action up: " + mWatchingForPull);
                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) 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) {
                    float hand = ev.getY() - mInitialTouchFocusY;
                    if (mHasPopped || hand > mPopLimit) {
                        if (!mHasPopped) {
                            vibrate(mPopDuration);
                            mHasPopped = true;
                        }
                        hand = hand + mOldHeight;
                        float target = hand;
                        float newHeight = clamp(target);
                        mScaler.setHeight(newHeight);
                        // glow if overscale
                        setGlow(calculateGlow(target, newHeight));
                    } else {
                        setGlow(calculateGlow(4f * hand, 0f));
                    }
                    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 +436,6 @@ 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;
            setView(v);
            setGlow(GLOW_BASE);
            mScaler.setView(v);
@@ -318,8 +450,10 @@ 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) {
@@ -337,7 +471,6 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
        mScaleAnimation.setFloatValues(h);
        mScaleAnimation.setupStartValues();
        mScaleAnimation.start();
        mStretching = false;
        setGlow(0f);
        mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight);
        if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView);
@@ -369,6 +502,17 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
    public void onClick(View v) {
        initScale(v);
        finishScale(true);
    }

    /**
     * Triggers haptic feedback.
     */
    private synchronized void vibrate(long duration) {
        if (mVibrator == null) {
            mVibrator = (android.os.Vibrator)
                    mContext.getSystemService(Context.VIBRATOR_SERVICE);
        }
        mVibrator.vibrate(duration);
    }
}
Loading