1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.quickstep.inputconsumers; 17 18 import static android.view.MotionEvent.ACTION_CANCEL; 19 import static android.view.MotionEvent.ACTION_DOWN; 20 import static android.view.MotionEvent.ACTION_MOVE; 21 import static android.view.MotionEvent.ACTION_POINTER_DOWN; 22 import static android.view.MotionEvent.ACTION_POINTER_UP; 23 import static android.view.MotionEvent.ACTION_UP; 24 25 import android.content.Context; 26 import android.view.Display; 27 import android.view.MotionEvent; 28 import android.view.VelocityTracker; 29 import android.view.ViewConfiguration; 30 31 import com.android.launcher3.R; 32 import com.android.quickstep.InputConsumer; 33 import com.android.quickstep.RecentsAnimationDeviceState; 34 import com.android.quickstep.SystemUiProxy; 35 import com.android.quickstep.util.MotionPauseDetector; 36 import com.android.systemui.shared.system.InputMonitorCompat; 37 38 /** 39 * Input consumer for two finger swipe actions for accessibility actions 40 */ 41 public class AccessibilityInputConsumer extends DelegateInputConsumer { 42 43 private static final String TAG = "A11yInputConsumer"; 44 45 private final Context mContext; 46 private final VelocityTracker mVelocityTracker; 47 private final MotionPauseDetector mMotionPauseDetector; 48 private final RecentsAnimationDeviceState mDeviceState; 49 50 private final float mMinGestureDistance; 51 private final float mMinFlingVelocity; 52 53 private int mActivePointerId = -1; 54 private float mDownY; 55 private float mTotalY; 56 AccessibilityInputConsumer(Context context, RecentsAnimationDeviceState deviceState, InputConsumer delegate, InputMonitorCompat inputMonitor)57 public AccessibilityInputConsumer(Context context, RecentsAnimationDeviceState deviceState, 58 InputConsumer delegate, InputMonitorCompat inputMonitor) { 59 super(delegate, inputMonitor); 60 mContext = context; 61 mVelocityTracker = VelocityTracker.obtain(); 62 mMinGestureDistance = context.getResources() 63 .getDimension(R.dimen.accessibility_gesture_min_swipe_distance); 64 mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity(); 65 mDeviceState = deviceState; 66 67 mMotionPauseDetector = new MotionPauseDetector(context); 68 } 69 70 @Override getType()71 public int getType() { 72 return TYPE_ACCESSIBILITY | mDelegate.getType(); 73 } 74 75 @Override onMotionEvent(MotionEvent ev)76 public void onMotionEvent(MotionEvent ev) { 77 if (mState != STATE_DELEGATE_ACTIVE) { 78 mVelocityTracker.addMovement(ev); 79 } 80 81 switch (ev.getActionMasked()) { 82 case ACTION_DOWN: { 83 break; 84 } 85 case ACTION_POINTER_UP: { 86 if (mState == STATE_ACTIVE) { 87 int pointerIndex = ev.getActionIndex(); 88 int pointerId = ev.getPointerId(pointerIndex); 89 if (pointerId == mActivePointerId) { 90 final int newPointerIdx = pointerIndex == 0 ? 1 : 0; 91 92 mTotalY += (ev.getY(pointerIndex) - mDownY); 93 mDownY = ev.getY(newPointerIdx); 94 mActivePointerId = ev.getPointerId(newPointerIdx); 95 } 96 } 97 break; 98 } 99 case ACTION_POINTER_DOWN: { 100 if (mState == STATE_INACTIVE) { 101 int pointerIndex = ev.getActionIndex(); 102 if (mDeviceState.getRotationTouchHelper() 103 .isInSwipeUpTouchRegion(ev, pointerIndex) 104 && mDelegate.allowInterceptByParent()) { 105 setActive(ev); 106 107 mActivePointerId = ev.getPointerId(pointerIndex); 108 mDownY = ev.getY(pointerIndex); 109 } else { 110 mState = STATE_DELEGATE_ACTIVE; 111 } 112 } 113 break; 114 } 115 case ACTION_MOVE: { 116 if (mState == STATE_ACTIVE && mDeviceState.isAccessibilityMenuShortcutAvailable()) { 117 int pointerIndex = ev.findPointerIndex(mActivePointerId); 118 if (pointerIndex == -1) { 119 break; 120 } 121 mMotionPauseDetector.addPosition(ev, pointerIndex); 122 } 123 break; 124 } 125 case ACTION_UP: 126 if (mState == STATE_ACTIVE) { 127 if (mDeviceState.isAccessibilityMenuShortcutAvailable() 128 && mMotionPauseDetector.isPaused()) { 129 SystemUiProxy.INSTANCE.get(mContext).notifyAccessibilityButtonLongClicked(); 130 } else { 131 mTotalY += (ev.getY() - mDownY); 132 mVelocityTracker.computeCurrentVelocity(1000); 133 134 if ((-mTotalY) > mMinGestureDistance 135 || (-mVelocityTracker.getYVelocity()) > mMinFlingVelocity) { 136 SystemUiProxy.INSTANCE.get(mContext).notifyAccessibilityButtonClicked( 137 Display.DEFAULT_DISPLAY); 138 } 139 } 140 } 141 // Follow through 142 case ACTION_CANCEL: { 143 mVelocityTracker.recycle(); 144 mMotionPauseDetector.clear(); 145 break; 146 } 147 } 148 149 if (mState != STATE_ACTIVE) { 150 mDelegate.onMotionEvent(ev); 151 } 152 } 153 } 154