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

Commit 6bbe7b78 authored by Thiago Vinícius Freire de Araújo Ribeiro's avatar Thiago Vinícius Freire de Araújo Ribeiro Committed by Gerrit Code Review
Browse files

Quick Settings: Swipe to switch

Adds the ability to switch to/from Quick Settings, using a sideways
swipe gesture.

Video demonstration: http://www.youtube.com/watch?v=jZaDVBIFAuA
(Video for the initial Patch Set only)

Patch Set 2: Flip animation follows your finger
Patch Set 3: Fix commit message
Patch Set 4: Fix commit message (again)
Patch Set 5: Pin down the statusbar during swipe
Patch Set 6: Smooth out animation after releasing swipe
Patch Set 7: Swipe in any direction to switch views
Patch Set 8: Ensure the statusbar is properly pinned down
Patch Set 9: Fix logic and event handling issues
Patch Set 10: Refactor for readability
Patch Set 11: Limit swipe detection to the bottom bar

Change-Id: I3cab45f72fe3c5fa40d4631fc6a84ea7bfe51bd1
parent 710e2c8e
Loading
Loading
Loading
Loading
+88 −1
Original line number Diff line number Diff line
@@ -33,6 +33,9 @@ public class NotificationPanelView extends PanelView {

    private static final float STATUS_BAR_SETTINGS_LEFT_PERCENTAGE = 0.8f;
    private static final float STATUS_BAR_SETTINGS_RIGHT_PERCENTAGE = 0.2f;
    private static final float STATUS_BAR_SWIPE_TRIGGER_PERCENTAGE = 0.05f;
    private static final float STATUS_BAR_SWIPE_VERTICAL_MAX_PERCENTAGE = 0.025f;
    private static final float STATUS_BAR_SWIPE_MOVE_PERCENTAGE = 0.2f;

    private Drawable mHandleBar;
    private float mHandleBarHeight;
@@ -41,6 +44,13 @@ public class NotificationPanelView extends PanelView {
    private PhoneStatusBar mStatusBar;
    private boolean mOkToFlip;

    private float mGestureStartX;
    private float mGestureStartY;
    private float mFlipOffset;
    private float mSwipeDirection;
    private boolean mTrackingSwipe;
    private boolean mSwipeTriggered;

    public NotificationPanelView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
@@ -95,10 +105,18 @@ public class NotificationPanelView extends PanelView {

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean shouldRecycleEvent = false;
        if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) {
            boolean flip = false;
            boolean swipeFlipJustFinished = false;
            boolean swipeFlipJustStarted = false;
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    mGestureStartX = event.getX(0);
                    mGestureStartY = event.getY(0);
                    mTrackingSwipe = isFullyExpanded() &&
                        // Pointer is at the handle portion of the view?
                        mGestureStartY > getHeight() - mHandleBarHeight - getPaddingBottom();
                    mOkToFlip = getExpandedHeight() == 0;
                    if (event.getX(0) > getWidth() * (1.0f - STATUS_BAR_SETTINGS_RIGHT_PERCENTAGE) &&
                            Settings.System.getInt(getContext().getContentResolver(),
@@ -110,9 +128,46 @@ public class NotificationPanelView extends PanelView {
                        flip = true;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    final float deltaX = Math.abs(event.getX(0) - mGestureStartX);
                    final float deltaY = Math.abs(event.getY(0) - mGestureStartY);
                    final float maxDeltaY = getHeight() * STATUS_BAR_SWIPE_VERTICAL_MAX_PERCENTAGE;
                    final float minDeltaX = getWidth() * STATUS_BAR_SWIPE_TRIGGER_PERCENTAGE;
                    if (mTrackingSwipe && deltaY > maxDeltaY) {
                        mTrackingSwipe = false;
                    }
                    if (mTrackingSwipe && deltaX > deltaY && deltaX > minDeltaX) {

                        // The value below can be used to adjust deltaX to always increase,
                        // if the user keeps swiping in the same direction as she started the
                        // gesture. If she, however, moves her finger the other way, deltaX will
                        // decrease.
                        //
                        // This allows for an horizontal swipe, in any direction, to always flip
                        // the views.
                        mSwipeDirection = event.getX(0) < mGestureStartX ? -1f : 1f;

                        if (mStatusBar.isShowingSettings()) {
                            mFlipOffset = 1f;
                            // in this case, however, we need deltaX to decrease
                            mSwipeDirection = -mSwipeDirection;
                        } else {
                            mFlipOffset = -1f;
                        }
                        mGestureStartX = event.getX(0);
                        mTrackingSwipe = false;
                        mSwipeTriggered = true;
                        swipeFlipJustStarted = true;
                    }
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    flip = true;
                    break;
                case MotionEvent.ACTION_UP:
                    swipeFlipJustFinished = mSwipeTriggered;
                    mSwipeTriggered = false;
                    mTrackingSwipe = false;
                    break;
            }
            if (mOkToFlip && flip) {
                float miny = event.getY(0);
@@ -130,8 +185,40 @@ public class NotificationPanelView extends PanelView {
                    }
                    mOkToFlip = false;
                }
            } else if (mSwipeTriggered) {
                final float deltaX = (event.getX(0) - mGestureStartX) * mSwipeDirection;
                mStatusBar.partialFlip(mFlipOffset +
                                       deltaX / (getWidth() * STATUS_BAR_SWIPE_MOVE_PERCENTAGE));
                if (!swipeFlipJustStarted) {
                    return true; // Consume the event.
                }
            } else if (swipeFlipJustFinished) {
                mStatusBar.completePartialFlip();
            }

            if (swipeFlipJustStarted || swipeFlipJustFinished) {
                // Made up event: finger at the middle bottom of the view.
                MotionEvent original = event;
                event = MotionEvent.obtain(original.getDownTime(), original.getEventTime(),
                    original.getAction(), getWidth()/2, getHeight(),
                    original.getPressure(0), original.getSize(0), original.getMetaState(),
                    original.getXPrecision(), original.getYPrecision(), original.getDeviceId(),
                    original.getEdgeFlags());

                // The following two lines looks better than the chunk of code above, but,
                // nevertheless, doesn't work. The view is not pinned down, and may close,
                // just after the gesture is finished.
                //
                // event = MotionEvent.obtainNoHistory(original);
                // event.setLocation(getWidth()/2, getHeight());
                shouldRecycleEvent = true;
            }

        }
        final boolean result = mHandleView.dispatchTouchEvent(event);
        if (shouldRecycleEvent) {
            event.recycle();
        }
        return mHandleView.dispatchTouchEvent(event);
        return result;
    }
}
+56 −7
Original line number Diff line number Diff line
@@ -1589,20 +1589,23 @@ public class PhoneStatusBar extends BaseStatusBar {
        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
        if (mClearButtonAnim != null) mClearButtonAnim.cancel();

        final boolean halfWayDone = mScrollView.getVisibility() == View.VISIBLE;
        final int zeroOutDelays = halfWayDone ? 0 : 1;

        // Only show the Power widget if it should be shown
        mPowerWidget.updateVisibility();

        mScrollView.setVisibility(View.VISIBLE);
        mScrollViewAnim = start(
            startDelay(FLIP_DURATION_OUT,
            startDelay(FLIP_DURATION_OUT * zeroOutDelays,
                interpolator(mDecelerateInterpolator,
                    ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f)
                    ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f)
                        .setDuration(FLIP_DURATION_IN)
                    )));
        mFlipSettingsViewAnim = start(
            setVisibilityWhenDone(
                interpolator(mAccelerateInterpolator,
                        ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f)
                        ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f)
                        )
                    .setDuration(FLIP_DURATION_OUT),
                mFlipSettingsView, View.INVISIBLE));
@@ -1657,6 +1660,50 @@ public class PhoneStatusBar extends BaseStatusBar {
        mClearButton.setVisibility(View.GONE);
    }

    public boolean isShowingSettings() {
        return mHasFlipSettings && mFlipSettingsView.getVisibility() == View.VISIBLE;
    }

    public void completePartialFlip() {
        if (mHasFlipSettings) {
            if (mFlipSettingsView.getVisibility() == View.VISIBLE) {
                flipToSettings();
            } else {
                flipToNotifications();
            }
        }
    }

    public void partialFlip(float progress) {
        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
        if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
        if (mClearButtonAnim != null) mClearButtonAnim.cancel();

        progress = Math.min(Math.max(progress, -1f), 1f);
        if (progress < 0f) { // notifications side
            mFlipSettingsView.setScaleX(0f);
            mFlipSettingsView.setVisibility(View.GONE);
            mSettingsButton.setVisibility(View.VISIBLE);
            mSettingsButton.setAlpha(-progress);
            mScrollView.setVisibility(View.VISIBLE);
            mScrollView.setScaleX(-progress);
            mPowerWidget.updateVisibility();
            mNotificationButton.setVisibility(View.GONE);
        } else { // settings side
            mFlipSettingsView.setScaleX(progress);
            mFlipSettingsView.setVisibility(View.VISIBLE);
            mSettingsButton.setVisibility(View.GONE);
            mScrollView.setVisibility(View.GONE);
            mScrollView.setScaleX(0f);
            mPowerWidget.setVisibility(View.GONE);
            mNotificationButton.setVisibility(View.VISIBLE);
            mNotificationButton.setAlpha(progress);
        }
        mClearButton.setVisibility(View.GONE);
    }

    public void flipToSettings() {
        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
@@ -1664,18 +1711,20 @@ public class PhoneStatusBar extends BaseStatusBar {
        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
        if (mClearButtonAnim != null) mClearButtonAnim.cancel();

        final boolean halfWayDone = mFlipSettingsView.getVisibility() == View.VISIBLE;
        final int zeroOutDelays = halfWayDone ? 0 : 1;

        mFlipSettingsView.setVisibility(View.VISIBLE);
        mFlipSettingsView.setScaleX(0f);
        mFlipSettingsViewAnim = start(
            startDelay(FLIP_DURATION_OUT,
            startDelay(FLIP_DURATION_OUT * zeroOutDelays,
                interpolator(mDecelerateInterpolator,
                    ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f)
                    ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f)
                        .setDuration(FLIP_DURATION_IN)
                    )));
        mScrollViewAnim = start(
            setVisibilityWhenDone(
                interpolator(mAccelerateInterpolator,
                        ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f)
                        ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f)
                        )
                    .setDuration(FLIP_DURATION_OUT),
                mScrollView, View.INVISIBLE));