• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.accessibility.magnification;
18 
19 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
20 import static android.view.MotionEvent.ACTION_CANCEL;
21 import static android.view.MotionEvent.ACTION_UP;
22 
23 import android.accessibilityservice.AccessibilityTrace;
24 import android.annotation.NonNull;
25 import android.util.Log;
26 import android.util.Slog;
27 import android.view.MotionEvent;
28 
29 import com.android.server.accessibility.AccessibilityTraceManager;
30 import com.android.server.accessibility.BaseEventStreamTransformation;
31 
32 import java.util.ArrayDeque;
33 import java.util.Queue;
34 
35 /**
36  * A base class that detects gestures and defines common methods for magnification.
37  */
38 public abstract class MagnificationGestureHandler extends BaseEventStreamTransformation {
39 
40     protected final String mLogTag = this.getClass().getSimpleName();
41     protected static final boolean DEBUG_ALL = Log.isLoggable("MagnificationGestureHandler",
42             Log.DEBUG);
43     protected static final boolean DEBUG_EVENT_STREAM = false | DEBUG_ALL;
44     private final Queue<MotionEvent> mDebugInputEventHistory;
45     private final Queue<MotionEvent> mDebugOutputEventHistory;
46 
47     /**
48      * The logical display id.
49      */
50     protected final int mDisplayId;
51 
52     /**
53      * {@code true} if this detector should be "triggerable" by some
54      * external shortcut invoking {@link #notifyShortcutTriggered},
55      * {@code false} if it should ignore such triggers.
56      */
57     protected final boolean mDetectShortcutTrigger;
58 
59     /**
60      * {@code true} if this detector should detect and respond to triple-tap
61      * gestures for engaging and disengaging magnification,
62      * {@code false} if it should ignore such gestures
63      */
64     protected final boolean mDetectTripleTap;
65 
66     /** Callback interface to report that magnification is interactive with a user. */
67     public interface Callback {
68         /**
69          * Called when the touch interaction is started by a user.
70          *
71          * @param displayId The logical display id
72          * @param mode The magnification mode
73          */
onTouchInteractionStart(int displayId, int mode)74         void onTouchInteractionStart(int displayId, int mode);
75 
76         /**
77          * Called when the touch interaction is ended by a user.
78          *
79          * @param displayId The logical display id
80          * @param mode The magnification mode
81          */
onTouchInteractionEnd(int displayId, int mode)82         void onTouchInteractionEnd(int displayId, int mode);
83     }
84 
85     private final AccessibilityTraceManager mTrace;
86     protected final Callback mCallback;
87 
MagnificationGestureHandler(int displayId, boolean detectTripleTap, boolean detectShortcutTrigger, AccessibilityTraceManager trace, @NonNull Callback callback)88     protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
89             boolean detectShortcutTrigger,
90             AccessibilityTraceManager trace,
91             @NonNull Callback callback) {
92         mDisplayId = displayId;
93         mDetectTripleTap = detectTripleTap;
94         mDetectShortcutTrigger = detectShortcutTrigger;
95         mTrace = trace;
96         mCallback = callback;
97 
98         mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
99         mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
100     }
101 
102     @Override
onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)103     public final void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
104         if (DEBUG_ALL) {
105             Slog.i(mLogTag, "onMotionEvent(" + event + ")");
106         }
107         if (mTrace.isA11yTracingEnabledForTypes(
108                 AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
109             mTrace.logTrace("MagnificationGestureHandler.onMotionEvent",
110                     AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
111                     "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
112         }
113         if (DEBUG_EVENT_STREAM) {
114             storeEventInto(mDebugInputEventHistory, event);
115         }
116         if (shouldDispatchTransformedEvent(event)) {
117             dispatchTransformedEvent(event, rawEvent, policyFlags);
118         } else {
119             onMotionEventInternal(event, rawEvent, policyFlags);
120 
121             final int action = event.getAction();
122             if (action == MotionEvent.ACTION_DOWN) {
123                 mCallback.onTouchInteractionStart(mDisplayId, getMode());
124             } else if (action == ACTION_UP || action == ACTION_CANCEL) {
125                 mCallback.onTouchInteractionEnd(mDisplayId, getMode());
126             }
127         }
128     }
129 
shouldDispatchTransformedEvent(MotionEvent event)130     private boolean shouldDispatchTransformedEvent(MotionEvent event) {
131         if ((!mDetectTripleTap && !mDetectShortcutTrigger) || !event.isFromSource(
132                 SOURCE_TOUCHSCREEN)) {
133             return true;
134         }
135         return false;
136     }
137 
dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)138     final void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
139         if (DEBUG_EVENT_STREAM) {
140             storeEventInto(mDebugOutputEventHistory, event);
141             try {
142                 super.onMotionEvent(event, rawEvent, policyFlags);
143                 return;
144             } catch (Exception e) {
145                 throw new RuntimeException(
146                         "Exception downstream following input events: " + mDebugInputEventHistory
147                                 + "\nTransformed into output events: " + mDebugOutputEventHistory,
148                         e);
149             }
150         }
151         super.onMotionEvent(event, rawEvent, policyFlags);
152     }
153 
storeEventInto(Queue<MotionEvent> queue, MotionEvent event)154     private static void storeEventInto(Queue<MotionEvent> queue, MotionEvent event) {
155         queue.add(MotionEvent.obtain(event));
156         // Prune old events
157         while (!queue.isEmpty() && (event.getEventTime() - queue.peek().getEventTime() > 5000)) {
158             queue.remove().recycle();
159         }
160     }
161 
162     /**
163      * Called when this MagnificationGestureHandler handles the motion event.
164      */
onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags)165     abstract void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags);
166 
167     /**
168      * Called when the shortcut target is magnification.
169      */
notifyShortcutTriggered()170     public void notifyShortcutTriggered() {
171         if (DEBUG_ALL) {
172             Slog.i(mLogTag, "notifyShortcutTriggered():");
173         }
174         if (mDetectShortcutTrigger) {
175             handleShortcutTriggered();
176         }
177     }
178 
179     /**
180      * Handles shortcut triggered event.
181      */
handleShortcutTriggered()182     abstract void handleShortcutTriggered();
183 
184     /**
185      * Indicates the magnification mode.
186      *
187      * @return the magnification mode of the handler
188      * @see android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
189      * @see android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
190      */
getMode()191     public abstract int getMode();
192 }
193