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

Commit c6e7a38f authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Adding support for accessibiliy gesture" into ub-launcher3-qt-dev

parents 03225dba 45801498
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -21,4 +21,6 @@
    <!-- The size of corner radius of the arrow in the arrow toast. -->
    <dimen name="arrow_toast_corner_radius">2dp</dimen>

    <!-- Minimum distance to swipe to trigger accessibility gesture -->
    <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
</resources>
 No newline at end of file
+156 −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;

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_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;

import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;

import com.android.launcher3.R;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.InputMonitorCompat;

/**
 * Touch consumer for two finger swipe actions for accessibility actions
 */
public class AccessibilityInputConsumer extends DelegateInputConsumer {

    private static final String TAG = "A11yInputConsumer";

    private final ISystemUiProxy mSystemUiProxy;
    private final VelocityTracker mVelocityTracker;
    private final MotionPauseDetector mMotionPauseDetector;
    private final boolean mAllowLongClick;

    private final float mMinGestureDistance;
    private final float mMinFlingVelocity;

    private int mActivePointerId = -1;
    private float mDownY;
    private float mTotalY;

    public AccessibilityInputConsumer(Context context, ISystemUiProxy systemUiProxy,
            boolean allowLongClick, InputConsumer delegate, InputMonitorCompat inputMonitor) {
        super(delegate, inputMonitor);
        mSystemUiProxy = systemUiProxy;
        mVelocityTracker = VelocityTracker.obtain();
        mMinGestureDistance = context.getResources()
                .getDimension(R.dimen.accessibility_gesture_min_swipe_distance);
        mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();

        mMotionPauseDetector = new MotionPauseDetector(context);
        mAllowLongClick = allowLongClick;
    }

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

    @Override
    public void onMotionEvent(MotionEvent ev) {
        if (mState != STATE_DELEGATE_ACTIVE) {
            mVelocityTracker.addMovement(ev);
        }

        switch (ev.getActionMasked()) {
            case ACTION_DOWN: {
                break;
            }
            case ACTION_POINTER_UP: {
                if (mState == STATE_ACTIVE) {
                    int pointerIndex = ev.getActionIndex();
                    int pointerId = ev.getPointerId(pointerIndex);
                    if (pointerId == mActivePointerId) {
                        final int newPointerIdx = pointerIndex == 0 ? 1 : 0;

                        mTotalY += (ev.getY(pointerIndex) - mDownY);
                        mDownY = ev.getY(newPointerIdx);
                        mActivePointerId = ev.getPointerId(newPointerIdx);
                    }
                }
                break;
            }
            case ACTION_POINTER_DOWN: {
                if (mState == STATE_INACTIVE) {
                    if (mDelegate.allowInterceptByParent()) {
                        setActive(ev);

                        int pointerIndex = ev.getActionIndex();
                        mActivePointerId = ev.getPointerId(pointerIndex);
                        mDownY = ev.getY(pointerIndex);
                    } else {
                        mState = STATE_DELEGATE_ACTIVE;
                    }
                }
                break;
            }
            case ACTION_MOVE: {
                if (mState == STATE_ACTIVE && mAllowLongClick) {
                    int pointerIndex = ev.findPointerIndex(mActivePointerId);
                    if (pointerIndex == -1) {
                        break;
                    }

                    mMotionPauseDetector.addPosition(ev.getY(pointerIndex) - mDownY,
                            ev.getEventTime());
                }
                break;
            }
            case ACTION_UP:
                if (mState == STATE_ACTIVE) {
                    try {
                        if (mAllowLongClick && mMotionPauseDetector.isPaused()) {
                            mSystemUiProxy.notifyAccessibilityButtonLongClicked();
                        } else {
                            mTotalY += (ev.getY() - mDownY);
                            mVelocityTracker.computeCurrentVelocity(1000);

                            if ((-mTotalY) > mMinGestureDistance
                                    || (-mVelocityTracker.getYVelocity()) > mMinFlingVelocity) {
                                mSystemUiProxy.notifyAccessibilityButtonClicked(
                                        Display.DEFAULT_DISPLAY);
                            }
                        }
                    } catch (RemoteException e) {
                        Log.e(TAG, "Unable to notify accessibility event", e);
                    }
                }
                // Follow through
            case ACTION_CANCEL: {
                mVelocityTracker.recycle();
                mMotionPauseDetector.clear();
                break;
            }
        }

        if (mState != STATE_ACTIVE) {
            mDelegate.onMotionEvent(ev);
        }
    }
}
+14 −52
Original line number Diff line number Diff line
@@ -50,20 +50,10 @@ import com.android.systemui.shared.system.QuickStepContract;
/**
 * Touch consumer for handling events to launch assistant from launcher
 */
public class AssistantTouchConsumer implements InputConsumer {
public class AssistantTouchConsumer extends DelegateInputConsumer {
    private static final String TAG = "AssistantTouchConsumer";
    private static final long RETRACT_ANIMATION_DURATION_MS = 300;

    /* The assistant touch consume competes with quick switch InputConsumer gesture. The delegate
     * can be chosen to run if the angle passing the slop is lower than the threshold angle. When
     * this occurs, the state changes to {@link #STATE_DELEGATE_ACTIVE} where the next incoming
     * motion events are handled by the delegate instead of the assistant touch consumer. If the
     * angle is higher than the threshold, the state will change to {@link #STATE_ASSISTANT_ACTIVE}.
     */
    private static final int STATE_INACTIVE = 0;
    private static final int STATE_ASSISTANT_ACTIVE = 1;
    private static final int STATE_DELEGATE_ACTIVE = 2;

    private static final String INVOCATION_TYPE_KEY = "invocation_type";
    private static final int INVOCATION_TYPE_GESTURE = 1;

@@ -78,7 +68,6 @@ public class AssistantTouchConsumer implements InputConsumer {
    private float mTimeFraction;
    private long mDragTime;
    private float mLastProgress;
    private int mState;
    private int mDirection;
    private ActivityControlHelper mActivityControlHelper;

@@ -87,46 +76,25 @@ public class AssistantTouchConsumer implements InputConsumer {
    private final int mAngleThreshold;
    private final float mSlop;
    private final ISystemUiProxy mSysUiProxy;
    private final InputConsumer mConsumerDelegate;
    private final Context mContext;

    private final InputMonitorCompat mInputMonitorCompat;


    public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
            InputConsumer delegate, InputMonitorCompat inputMonitorCompat,
            ActivityControlHelper activityControlHelper) {
            ActivityControlHelper activityControlHelper, InputConsumer delegate,
            InputMonitorCompat inputMonitor) {
        super(delegate, inputMonitor);
        final Resources res = context.getResources();
        mContext = context;
        mSysUiProxy = systemUiProxy;
        mConsumerDelegate = delegate;
        mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
        mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
        mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
        mSlop = QuickStepContract.getQuickStepDragSlopPx();
        mInputMonitorCompat = inputMonitorCompat;
        mActivityControlHelper = activityControlHelper;
        mState = STATE_INACTIVE;
    }

    @Override
    public int getType() {
        return TYPE_ASSISTANT;
    }

    @Override
    public boolean useSharedSwipeState() {
        if (mConsumerDelegate != null) {
            return mConsumerDelegate.useSharedSwipeState();
        }
        return false;
    }

    @Override
    public void onConsumerAboutToBeSwitched() {
        if (mConsumerDelegate != null) {
            mConsumerDelegate.onConsumerAboutToBeSwitched();
        }
        return TYPE_ASSISTANT | mDelegate.getType();
    }

    @Override
@@ -158,6 +126,10 @@ public class AssistantTouchConsumer implements InputConsumer {
                if (mState == STATE_DELEGATE_ACTIVE) {
                    break;
                }
                if (!mDelegate.allowInterceptByParent()) {
                    mState = STATE_DELEGATE_ACTIVE;
                    break;
                }
                int pointerIndex = ev.findPointerIndex(mActivePointerId);
                if (pointerIndex == -1) {
                    break;
@@ -168,9 +140,6 @@ public class AssistantTouchConsumer implements InputConsumer {
                    // Normal gesture, ensure we pass the slop before we start tracking the gesture
                    if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) > mSlop) {

                        // Cancel touches to other windows (intercept)
                        mInputMonitorCompat.pilferPointers();

                        mPassedSlop = true;
                        mStartDragPos.set(mLastPos.x, mLastPos.y);
                        mDragTime = SystemClock.uptimeMillis();
@@ -182,15 +151,7 @@ public class AssistantTouchConsumer implements InputConsumer {
                        angle = angle > 90 ? 180 - angle : angle;

                        if (angle > mAngleThreshold && angle < 90) {
                            mState = STATE_ASSISTANT_ACTIVE;

                            if (mConsumerDelegate != null) {
                                // Send cancel event
                                MotionEvent event = MotionEvent.obtain(ev);
                                event.setAction(MotionEvent.ACTION_CANCEL);
                                mConsumerDelegate.onMotionEvent(event);
                                event.recycle();
                            }
                            setActive(ev);
                        } else {
                            mState = STATE_DELEGATE_ACTIVE;
                        }
@@ -232,8 +193,8 @@ public class AssistantTouchConsumer implements InputConsumer {
                break;
        }

        if (mState != STATE_ASSISTANT_ACTIVE && mConsumerDelegate != null) {
            mConsumerDelegate.onMotionEvent(ev);
        if (mState != STATE_ACTIVE) {
            mDelegate.onMotionEvent(ev);
        }
    }

@@ -249,7 +210,8 @@ public class AssistantTouchConsumer implements InputConsumer {
                    Bundle args = new Bundle();
                    args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);

                    BaseDraggingActivity launcherActivity = mActivityControlHelper.getCreatedActivity();
                    BaseDraggingActivity launcherActivity =
                            mActivityControlHelper.getCreatedActivity();
                    if (launcherActivity != null) {
                        launcherActivity.getRootView().
                                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+49 −0
Original line number Diff line number Diff line
package com.android.quickstep;

import android.view.MotionEvent;

import com.android.systemui.shared.system.InputMonitorCompat;

public abstract class DelegateInputConsumer implements InputConsumer {

    protected static final int STATE_INACTIVE = 0;
    protected static final int STATE_ACTIVE = 1;
    protected static final int STATE_DELEGATE_ACTIVE = 2;

    protected final InputConsumer mDelegate;
    protected final InputMonitorCompat mInputMonitor;

    protected int mState;

    public DelegateInputConsumer(InputConsumer delegate, InputMonitorCompat inputMonitor) {
        mDelegate = delegate;
        mInputMonitor = inputMonitor;
        mState = STATE_INACTIVE;
    }

    @Override
    public boolean useSharedSwipeState() {
        return mDelegate.useSharedSwipeState();
    }

    @Override
    public boolean allowInterceptByParent() {
        return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
    }

    @Override
    public void onConsumerAboutToBeSwitched() {
        mDelegate.onConsumerAboutToBeSwitched();
    }

    protected void setActive(MotionEvent ev) {
        mState = STATE_ACTIVE;
        mInputMonitor.pilferPointers();

        // Send cancel event
        MotionEvent event = MotionEvent.obtain(ev);
        event.setAction(MotionEvent.ACTION_CANCEL);
        mDelegate.onMotionEvent(event);
        event.recycle();
    }
}
+13 −5
Original line number Diff line number Diff line
@@ -24,11 +24,12 @@ import android.view.MotionEvent;
@TargetApi(Build.VERSION_CODES.O)
public interface InputConsumer {

    int TYPE_NO_OP = 0;
    int TYPE_OVERVIEW = 1;
    int TYPE_OTHER_ACTIVITY = 2;
    int TYPE_ASSISTANT = 3;
    int TYPE_DEVICE_LOCKED = 4;
    int TYPE_NO_OP = 1 << 0;
    int TYPE_OVERVIEW = 1 << 1;
    int TYPE_OTHER_ACTIVITY = 1 << 2;
    int TYPE_ASSISTANT = 1 << 3;
    int TYPE_DEVICE_LOCKED = 1 << 4;
    int TYPE_ACCESSIBILITY = 1 << 5;

    InputConsumer NO_OP = () -> TYPE_NO_OP;

@@ -38,6 +39,13 @@ public interface InputConsumer {
        return false;
    }

    /**
     * Returns true if the user has crossed the threshold for it to be an explicit action.
     */
    default boolean allowInterceptByParent() {
        return true;
    }

    /**
     * Called by the event queue when the consumer is about to be switched to a new consumer.
     */
Loading