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

Commit ce5fdc76 authored by Ricardo Cerqueira's avatar Ricardo Cerqueira
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

Conflicts:
	packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
parent ec94ab09
Loading
Loading
Loading
Loading
+88 −1
Original line number Original line Diff line number Diff line
@@ -36,6 +36,9 @@ public class NotificationPanelView extends PanelView {


    private static final float STATUS_BAR_SETTINGS_LEFT_PERCENTAGE = 0.8f;
    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_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 Drawable mHandleBar;
    private float mHandleBarHeight;
    private float mHandleBarHeight;
@@ -44,6 +47,13 @@ public class NotificationPanelView extends PanelView {
    private PhoneStatusBar mStatusBar;
    private PhoneStatusBar mStatusBar;
    private boolean mOkToFlip;
    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) {
    public NotificationPanelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        super(context, attrs);
    }
    }
@@ -107,6 +117,7 @@ public class NotificationPanelView extends PanelView {


    @Override
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    public boolean onTouchEvent(MotionEvent event) {
        boolean shouldRecycleEvent = false;
        if (DEBUG_GESTURES) {
        if (DEBUG_GESTURES) {
            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
                EventLog.writeEvent(EventLogTags.SYSUI_NOTIFICATIONPANEL_TOUCH,
                EventLog.writeEvent(EventLogTags.SYSUI_NOTIFICATIONPANEL_TOUCH,
@@ -115,8 +126,15 @@ public class NotificationPanelView extends PanelView {
        }
        }
        if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) {
        if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) {
            boolean flip = false;
            boolean flip = false;
            boolean swipeFlipJustFinished = false;
            boolean swipeFlipJustStarted = false;
            switch (event.getActionMasked()) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                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;
                    mOkToFlip = getExpandedHeight() == 0;
                    if (event.getX(0) > getWidth() * (1.0f - STATUS_BAR_SETTINGS_RIGHT_PERCENTAGE) &&
                    if (event.getX(0) > getWidth() * (1.0f - STATUS_BAR_SETTINGS_RIGHT_PERCENTAGE) &&
                            Settings.System.getInt(getContext().getContentResolver(),
                            Settings.System.getInt(getContext().getContentResolver(),
@@ -128,9 +146,46 @@ public class NotificationPanelView extends PanelView {
                        flip = true;
                        flip = true;
                    }
                    }
                    break;
                    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:
                case MotionEvent.ACTION_POINTER_DOWN:
                    flip = true;
                    flip = true;
                    break;
                    break;
                case MotionEvent.ACTION_UP:
                    swipeFlipJustFinished = mSwipeTriggered;
                    mSwipeTriggered = false;
                    mTrackingSwipe = false;
                    break;
            }
            }
            if (mOkToFlip && flip) {
            if (mOkToFlip && flip) {
                float miny = event.getY(0);
                float miny = event.getY(0);
@@ -148,8 +203,40 @@ public class NotificationPanelView extends PanelView {
                    }
                    }
                    mOkToFlip = false;
                    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 Original line Diff line number Diff line
@@ -1677,20 +1677,23 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
        if (mClearButtonAnim != null) mClearButtonAnim.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
        // Only show the Power widget if it should be shown
        mPowerWidget.updateVisibility();
        mPowerWidget.updateVisibility();


        mScrollView.setVisibility(View.VISIBLE);
        mScrollView.setVisibility(View.VISIBLE);
        mScrollViewAnim = start(
        mScrollViewAnim = start(
            startDelay(FLIP_DURATION_OUT,
            startDelay(FLIP_DURATION_OUT * zeroOutDelays,
                interpolator(mDecelerateInterpolator,
                interpolator(mDecelerateInterpolator,
                    ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f)
                    ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f)
                        .setDuration(FLIP_DURATION_IN)
                        .setDuration(FLIP_DURATION_IN)
                    )));
                    )));
        mFlipSettingsViewAnim = start(
        mFlipSettingsViewAnim = start(
            setVisibilityWhenDone(
            setVisibilityWhenDone(
                interpolator(mAccelerateInterpolator,
                interpolator(mAccelerateInterpolator,
                        ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f)
                        ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f)
                        )
                        )
                    .setDuration(FLIP_DURATION_OUT),
                    .setDuration(FLIP_DURATION_OUT),
                mFlipSettingsView, View.INVISIBLE));
                mFlipSettingsView, View.INVISIBLE));
@@ -1751,6 +1754,50 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
        mClearButton.setVisibility(View.GONE);
        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() {
    public void flipToSettings() {
        // Settings are not available in setup
        // Settings are not available in setup
        if (!mUserSetup) return;
        if (!mUserSetup) return;
@@ -1761,18 +1808,20 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
        if (mClearButtonAnim != null) mClearButtonAnim.cancel();


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

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