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

Commit 18b76be7 authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge changes from topic "ub-one-handed-rebase" into ub-launcher3-master

* changes:
  Guard swipe to notification gesture with system settings preference
  Update state and touch region after one handed overlay changed
  Hook one-handed gesture to expand notification panel by default
  Enable one-handed mode gestural in QuickStep
parents efa41c1c fbb8c69a
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import androidx.annotation.WorkerThread;

import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
@@ -74,6 +75,7 @@ import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
import com.android.quickstep.inputconsumers.OverscrollInputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
@@ -297,6 +299,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
        mAM = ActivityManagerWrapper.getInstance();
        mDeviceState = new RecentsAnimationDeviceState(this);
        mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
        mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
        mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
        mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
        ProtoTracer.INSTANCE.get(this).add(this);
@@ -338,6 +341,13 @@ public class TouchInteractionService extends Service implements PluginListener<O
        resetHomeBounceSeenOnQuickstepEnabledFirstTime();
    }

    /**
     * Called when the one handed mode overlay package changes, to recreate touch region.
     */
    private void onOneHandedModeOverlayChanged(int newGesturalHeight) {
        initInputMonitor();
    }

    @UiThread
    public void onUserUnlocked() {
        mTaskAnimationManager = new TaskAnimationManager();
@@ -500,6 +510,11 @@ public class TouchInteractionService extends Service implements PluginListener<O
                            mGestureState,
                            InputConsumer.NO_OP, mInputMonitorCompat,
                            mOverviewComponentObserver.assistantGestureIsConstrained());
                } else if (mDeviceState.canTriggerOneHandedAction(event)
                    && !mDeviceState.isOneHandedModeActive()) {
                    // Consume gesture event for triggering one handed feature.
                    mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
                        InputConsumer.NO_OP, mInputMonitorCompat);
                } else {
                    mUncheckedConsumer = InputConsumer.NO_OP;
                }
@@ -627,6 +642,11 @@ public class TouchInteractionService extends Service implements PluginListener<O
                base = new ScreenPinnedInputConsumer(this, newGestureState);
            }

            if (mDeviceState.canTriggerOneHandedAction(event)) {
                base = new OneHandedModeInputConsumer(this, mDeviceState, base,
                        mInputMonitorCompat);
            }

            if (mDeviceState.isAccessibilityMenuAvailable()) {
                base = new AccessibilityInputConsumer(this, mDeviceState, base,
                        mInputMonitorCompat);
@@ -635,6 +655,11 @@ public class TouchInteractionService extends Service implements PluginListener<O
            if (mDeviceState.isScreenPinningActive()) {
                base = mResetGestureInputConsumer;
            }

            if (mDeviceState.canTriggerOneHandedAction(event)) {
                base = new OneHandedModeInputConsumer(this, mDeviceState, base,
                        mInputMonitorCompat);
            }
        }
        return base;
    }
@@ -796,6 +821,13 @@ public class TouchInteractionService extends Service implements PluginListener<O
        }
        if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(),
                activity.getResources().getConfiguration().diff(newConfig))) {
            // Since navBar gestural height are different between portrait and landscape,
            // can handle orientation changes and refresh navigation gestural region through
            // onOneHandedModeChanged()
            int newGesturalHeight = ResourceUtils.getNavbarSize(
                    ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
                    getApplicationContext().getResources());
            mDeviceState.onOneHandedModeChanged(newGesturalHeight);
            return;
        }

+159 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.quickstep.inputconsumers;

import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;

import static com.android.launcher3.Utilities.squaredHypot;

import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;

import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.InputMonitorCompat;

/**
 * Touch consumer for handling gesture event to launch one handed
 * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode
 */
public class OneHandedModeInputConsumer extends DelegateInputConsumer {

    private static final String TAG = "OneHandedModeInputConsumer";
    private static final int ANGLE_MAX = 150;
    private static final int ANGLE_MIN = 30;

    private final Context mContext;
    private final RecentsAnimationDeviceState mDeviceState;

    private final float mDragDistThreshold;
    private final float mSquaredSlop;

    private final PointF mDownPos = new PointF();
    private final PointF mLastPos = new PointF();
    private final PointF mStartDragPos = new PointF();

    private boolean mPassedSlop;

    public OneHandedModeInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
            InputConsumer delegate, InputMonitorCompat inputMonitor) {
        super(delegate, inputMonitor);
        mContext = context;
        mDeviceState = deviceState;
        mDragDistThreshold = context.getResources().getDimensionPixelSize(
                R.dimen.gestures_onehanded_drag_threshold);
        mSquaredSlop = Utilities.squaredTouchSlop(context);
    }

    @Override
    public int getType() {
        return TYPE_ONE_HANDED | mDelegate.getType();
    }

    @Override
    public void onMotionEvent(MotionEvent ev) {
        switch (ev.getActionMasked()) {
            case ACTION_DOWN: {
                mDownPos.set(ev.getX(), ev.getY());
                mLastPos.set(mDownPos);
                break;
            }
            case ACTION_MOVE: {
                if (mState == STATE_DELEGATE_ACTIVE) {
                    break;
                }
                if (!mDelegate.allowInterceptByParent()) {
                    mState = STATE_DELEGATE_ACTIVE;
                    break;
                }

                mLastPos.set(ev.getX(), ev.getY());
                if (!mPassedSlop) {
                    if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
                            > mSquaredSlop) {
                        mStartDragPos.set(mLastPos.x, mLastPos.y);
                        if ((!mDeviceState.isOneHandedModeActive() && isValidStartAngle(
                                mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y))
                                || (mDeviceState.isOneHandedModeActive() && isValidExitAngle(
                                mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y))) {
                            mPassedSlop = true;
                            setActive(ev);
                        } else {
                            mState = STATE_DELEGATE_ACTIVE;
                        }
                    }
                } else {
                    float distance = (float) Math.hypot(mLastPos.x - mStartDragPos.x,
                            mLastPos.y - mStartDragPos.y);
                    if (distance > mDragDistThreshold && mPassedSlop) {
                        onStopGestureDetected();
                    }
                }
                break;
            }
            case ACTION_UP:
            case ACTION_CANCEL: {
                if (mLastPos.y >= mStartDragPos.y && mPassedSlop) {
                    onStartGestureDetected();
                }

                mPassedSlop = false;
                mState = STATE_INACTIVE;
                break;
            }
        }

        if (mState != STATE_ACTIVE) {
            mDelegate.onMotionEvent(ev);
        }
    }

    private void onStartGestureDetected() {
        if (mDeviceState.isOneHandedModeEnabled()) {
            if (!mDeviceState.isOneHandedModeActive()) {
                SystemUiProxy.INSTANCE.get(mContext).startOneHandedMode();
            }
        } else if (mDeviceState.isSwipeToNotificationEnabled()) {
            SystemUiProxy.INSTANCE.get(mContext).expandNotificationPanel();
        }
    }

    private void onStopGestureDetected() {
        if (!mDeviceState.isOneHandedModeEnabled() || !mDeviceState.isOneHandedModeActive()) {
            return;
        }

        SystemUiProxy.INSTANCE.get(mContext).stopOneHandedMode();
    }

    private boolean isValidStartAngle(float deltaX, float deltaY) {
        final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
        return angle > -(ANGLE_MAX) && angle < -(ANGLE_MIN);
    }

    private boolean isValidExitAngle(float deltaX, float deltaY) {
        final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
        return angle > ANGLE_MIN && angle < ANGLE_MAX;
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -76,6 +76,10 @@
    <dimen name="gestures_assistant_drag_threshold">55dp</dimen>
    <dimen name="gestures_assistant_fling_threshold">55dp</dimen>

    <!-- One-Handed Mode -->
    <!-- Threshold for draging distance to enable one-handed mode -->
    <dimen name="gestures_onehanded_drag_threshold">20dp</dimen>

    <!-- Distance to move elements when swiping up to go home from launcher -->
    <dimen name="home_pullback_distance">28dp</dimen>

+3 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ public interface InputConsumer {
    int TYPE_RESET_GESTURE = 1 << 8;
    int TYPE_OVERSCROLL = 1 << 9;
    int TYPE_SYSUI_OVERLAY = 1 << 10;
    int TYPE_ONE_HANDED = 1 << 11;

    String[] NAMES = new String[] {
           "TYPE_NO_OP",                    // 0
@@ -47,7 +48,8 @@ public interface InputConsumer {
            "TYPE_OVERVIEW_WITHOUT_FOCUS",  // 7
            "TYPE_RESET_GESTURE",           // 8
            "TYPE_OVERSCROLL",              // 9
            "TYPE_SYSUI_OVERLAY"         // 10
            "TYPE_SYSUI_OVERLAY",           // 10
            "TYPE_ONE_HANDED",              // 11
    };

    InputConsumer NO_OP = () -> TYPE_NO_OP;
+32 −8
Original line number Diff line number Diff line
@@ -63,7 +63,9 @@ class OrientationTouchTransformer {
    private SparseArray<OrientationRectF> mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS);
    private final RectF mAssistantLeftRegion = new RectF();
    private final RectF mAssistantRightRegion = new RectF();
    private final RectF mOneHandedModeRegion = new RectF();
    private int mCurrentDisplayRotation;
    private int mNavBarGesturalHeight;
    private boolean mEnableMultipleRegions;
    private Resources mResources;
    private OrientationRectF mLastRectTouched;
@@ -103,20 +105,35 @@ class OrientationTouchTransformer {
        mResources = resources;
        mMode = mode;
        mContractInfo = contractInfo;
        mNavBarGesturalHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
    }

    void setNavigationMode(SysUINavigationMode.Mode newMode, DefaultDisplay.Info info) {
        if (mMode == newMode) {
            return;
        }
        this.mMode = newMode;
    private void refreshTouchRegion(DefaultDisplay.Info info, Resources newRes) {
        // Swipe touch regions are independent of nav mode, so we have to clear them explicitly
        // here to avoid, for ex, a nav region for 2-button rotation 0 being used for 3-button mode
        // It tries to cache and reuse swipe regions whenever possible based only on rotation
        mResources = newRes;
        mSwipeTouchRegions.clear();
        resetSwipeRegions(info);
    }

    void setNavigationMode(SysUINavigationMode.Mode newMode, DefaultDisplay.Info info,
            Resources newRes) {
        if (mMode == newMode) {
            return;
        }
        this.mMode = newMode;
        refreshTouchRegion(info, newRes);
    }

    void setGesturalHeight(int newGesturalHeight, DefaultDisplay.Info info, Resources newRes) {
        if (mNavBarGesturalHeight == newGesturalHeight) {
            return;
        }
        mNavBarGesturalHeight = newGesturalHeight;
        refreshTouchRegion(info, newRes);
    }

    /**
     * Sets the current nav bar region to listen to events for as determined by
     * {@param info}. If multiple nav bar regions are enabled, then this region will be added
@@ -216,10 +233,10 @@ class OrientationTouchTransformer {

        Point size = display.realSize;
        int rotation = display.rotation;
        int touchHeight = mNavBarGesturalHeight;
        OrientationRectF orientationRectF =
                new OrientationRectF(0, 0, size.x, size.y, rotation);
        if (mMode == SysUINavigationMode.Mode.NO_BUTTON) {
            int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
            orientationRectF.top = orientationRectF.bottom - touchHeight;
            updateAssistantRegions(orientationRectF);
        } else {
@@ -235,10 +252,11 @@ class OrientationTouchTransformer {
                            + getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
                    break;
                default:
                    orientationRectF.top = orientationRectF.bottom
                            - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
                    orientationRectF.top = orientationRectF.bottom - touchHeight;
            }
        }
        // One handed gestural only active on portrait mode
        mOneHandedModeRegion.set(0, orientationRectF.bottom - touchHeight, size.x, size.y);

        return orientationRectF;
    }
@@ -264,6 +282,10 @@ class OrientationTouchTransformer {

    }

    boolean touchInOneHandedModeRegion(MotionEvent ev) {
        return mOneHandedModeRegion.contains(ev.getX(), ev.getY());
    }

    private int getNavbarSize(String resName) {
        return ResourceUtils.getNavbarSize(resName, mResources);
    }
@@ -355,6 +377,8 @@ class OrientationTouchTransformer {
            regions.append(rectF.mRotation).append(" ");
        }
        pw.println(regions.toString());
        pw.println("  mNavBarGesturalHeight=" + mNavBarGesturalHeight);
        pw.println("  mOneHandedModeRegion=" + mOneHandedModeRegion);
    }

    private class OrientationRectF extends RectF {
Loading