• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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_MOVE;
22 import static android.view.MotionEvent.ACTION_UP;
23 
24 import static java.util.Arrays.asList;
25 import static java.util.Arrays.copyOfRange;
26 
27 import android.annotation.Nullable;
28 import android.annotation.UiContext;
29 import android.content.Context;
30 import android.graphics.Point;
31 import android.os.SystemClock;
32 import android.provider.Settings;
33 import android.util.MathUtils;
34 import android.util.Slog;
35 import android.view.Display;
36 import android.view.MotionEvent;
37 
38 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.server.accessibility.AccessibilityTraceManager;
41 import com.android.server.accessibility.EventStreamTransformation;
42 import com.android.server.accessibility.Flags;
43 import com.android.server.accessibility.gestures.GestureMatcher;
44 import com.android.server.accessibility.gestures.MultiFingerMultiTap;
45 import com.android.server.accessibility.gestures.MultiFingerMultiTapAndHold;
46 import com.android.server.accessibility.gestures.MultiTap;
47 import com.android.server.accessibility.gestures.MultiTapAndHold;
48 
49 import java.util.ArrayList;
50 import java.util.List;
51 
52 /**
53  * This class handles window magnification in response to touch events and shortcut.
54  *
55  * The behavior is as follows:
56  *
57  * <ol>
58  *   <li> 1. Toggle Window magnification by triple-tap gesture shortcut. It is triggered via
59  *   {@link #onTripleTap(MotionEvent)}.
60  *   <li> 2. Toggle Window magnification by tapping shortcut. It is triggered via
61  *   {@link #notifyShortcutTriggered()}.
62  *   <li> When the window magnifier is visible, pinching with any number of additional fingers
63  *   would adjust the magnification scale .<strong>Note</strong> that this operation is valid only
64  *   when at least one finger is in the window.
65  *   <li> When the window magnifier is visible, to do scrolling to move the window magnifier,
66  *   the user can use two or more fingers and at least one of them is inside the window.
67  *   <br><strong>Note</strong> that the offset of this callback is opposed to moving direction.
68  *  The operation becomes invalid after performing scaling operation until all fingers are
69  *  lifted.
70  * </ol>
71  */
72 @SuppressWarnings("WeakerAccess")
73 public class WindowMagnificationGestureHandler extends MagnificationGestureHandler {
74 
75     private static final boolean DEBUG_STATE_TRANSITIONS = false | DEBUG_ALL;
76     private static final boolean DEBUG_DETECTING = false | DEBUG_ALL;
77 
78     //Ensure the range has consistency with FullScreenMagnificationGestureHandler.
79     private static final float MIN_SCALE = 1.0f;
80     private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;
81 
82     private final MagnificationConnectionManager mMagnificationConnectionManager;
83     @VisibleForTesting
84     final DelegatingState mDelegatingState;
85     @VisibleForTesting
86     final DetectingState mDetectingState;
87     @VisibleForTesting
88     final PanningScalingGestureState mObservePanningScalingState;
89     @VisibleForTesting
90     final ViewportDraggingState mViewportDraggingState;
91 
92     @VisibleForTesting
93     State mCurrentState;
94     @VisibleForTesting
95     State mPreviousState;
96 
97     private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
98     private final Context mContext;
99     private final Point mTempPoint = new Point();
100 
101     private long mTripleTapAndHoldStartedTime = 0;
102 
WindowMagnificationGestureHandler(@iContext Context context, MagnificationConnectionManager magnificationConnectionManager, AccessibilityTraceManager trace, Callback callback, boolean detectSingleFingerTripleTap, boolean detectTwoFingerTripleTap, boolean detectShortcutTrigger, int displayId)103     public WindowMagnificationGestureHandler(@UiContext Context context,
104             MagnificationConnectionManager magnificationConnectionManager,
105             AccessibilityTraceManager trace,
106             Callback callback,
107             boolean detectSingleFingerTripleTap,
108             boolean detectTwoFingerTripleTap,
109             boolean detectShortcutTrigger,
110             int displayId) {
111         super(displayId, detectSingleFingerTripleTap, detectTwoFingerTripleTap,
112                 detectShortcutTrigger, trace, callback);
113         if (DEBUG_ALL) {
114             Slog.i(mLogTag,
115                     "WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
116         }
117         mContext = context;
118         mMagnificationConnectionManager = magnificationConnectionManager;
119         mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context,
120                 (event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent,
121                         policyFlags));
122         mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate);
123         mDetectingState = new DetectingState(context);
124         mViewportDraggingState = new ViewportDraggingState();
125         mObservePanningScalingState = new PanningScalingGestureState(
126                 new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true,
127                         new PanningScalingHandler.MagnificationDelegate() {
128                             @Override
129                             public boolean processScroll(int displayId, float distanceX,
130                                     float distanceY) {
131                                 return mMagnificationConnectionManager.processScroll(
132                                         displayId, distanceX, distanceY);
133                             }
134 
135                             @Override
136                             public void setScale(int displayId, float scale) {
137                                 mMagnificationConnectionManager.setScale(displayId, scale);
138                             }
139 
140                             @Override
141                             public float getScale(int displayId) {
142                                 return mMagnificationConnectionManager.getScale(displayId);
143                             }
144                         }));
145 
146         transitionTo(mDetectingState);
147     }
148 
149     @Override
handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)150     void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
151         // Window Magnification viewport doesn't move with mouse events (yet).
152     }
153 
154     @Override
onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags)155     void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
156         if (event.getSource() != SOURCE_TOUCHSCREEN) {
157             return;
158         }
159         // To keep InputEventConsistencyVerifiers within GestureDetectors happy.
160         mObservePanningScalingState.mPanningScalingHandler.onTouchEvent(event);
161         mCurrentState.onMotionEvent(event, rawEvent, policyFlags);
162     }
163 
164     @Override
clearEvents(int inputSource)165     public void clearEvents(int inputSource) {
166         if (inputSource == SOURCE_TOUCHSCREEN) {
167             resetToDetectState();
168         }
169         super.clearEvents(inputSource);
170     }
171 
172     @Override
onDestroy()173     public void onDestroy() {
174         if (DEBUG_ALL) {
175             Slog.i(mLogTag, "onDestroy(); delayed = "
176                     + mDetectingState.toString());
177         }
178         mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
179         resetToDetectState();
180     }
181 
182     @Override
handleShortcutTriggered()183     public void handleShortcutTriggered() {
184         final Point screenSize = mTempPoint;
185         getScreenSize(mTempPoint);
186         toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f,
187                 MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
188     }
189 
getScreenSize(Point outSize)190     private  void getScreenSize(Point outSize) {
191         final Display display = mContext.getDisplay();
192         display.getRealSize(outSize);
193     }
194 
195     @Override
getMode()196     public int getMode() {
197         return Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
198     }
199 
enableWindowMagnifier(float centerX, float centerY, @MagnificationConnectionManager.WindowPosition int windowPosition)200     private void enableWindowMagnifier(float centerX, float centerY,
201             @MagnificationConnectionManager.WindowPosition int windowPosition) {
202         if (DEBUG_ALL) {
203             Slog.i(mLogTag, "enableWindowMagnifier :"
204                     + centerX + ", " + centerY + ", " + windowPosition);
205         }
206 
207         final float scale = MathUtils.constrain(
208                 mMagnificationConnectionManager.getPersistedScale(mDisplayId),
209                 MIN_SCALE, MAX_SCALE);
210         mMagnificationConnectionManager.enableWindowMagnification(
211                 mDisplayId, scale, centerX, centerY, windowPosition);
212     }
213 
disableWindowMagnifier()214     private void disableWindowMagnifier() {
215         if (DEBUG_ALL) {
216             Slog.i(mLogTag, "disableWindowMagnifier()");
217         }
218         mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, false);
219     }
220 
toggleMagnification(float centerX, float centerY, @MagnificationConnectionManager.WindowPosition int windowPosition)221     private void toggleMagnification(float centerX, float centerY,
222             @MagnificationConnectionManager.WindowPosition int windowPosition) {
223         if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)) {
224             disableWindowMagnifier();
225         } else {
226             enableWindowMagnifier(centerX, centerY, windowPosition);
227         }
228     }
229 
onTripleTap(MotionEvent up)230     private void onTripleTap(MotionEvent up) {
231         if (DEBUG_DETECTING) {
232             Slog.i(mLogTag, "onTripleTap()");
233         }
234         toggleMagnification(up.getX(), up.getY(),
235                 MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
236     }
237 
238     @VisibleForTesting
onTripleTapAndHold(MotionEvent up)239     void onTripleTapAndHold(MotionEvent up) {
240         if (DEBUG_DETECTING) {
241             Slog.i(mLogTag, "onTripleTapAndHold()");
242         }
243         mViewportDraggingState.mEnabledBeforeDrag =
244                 mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId);
245         enableWindowMagnifier(up.getX(), up.getY(),
246                 MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
247         mTripleTapAndHoldStartedTime = SystemClock.uptimeMillis();
248         transitionTo(mViewportDraggingState);
249     }
250 
251     @VisibleForTesting
releaseTripleTapAndHold()252     void releaseTripleTapAndHold() {
253         if (!mViewportDraggingState.mEnabledBeforeDrag) {
254             mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
255         }
256         transitionTo(mDetectingState);
257         if (mTripleTapAndHoldStartedTime != 0) {
258             final long duration = SystemClock.uptimeMillis() - mTripleTapAndHoldStartedTime;
259             logMagnificationTripleTapAndHoldSession(duration);
260             mTripleTapAndHoldStartedTime = 0;
261         }
262     }
263 
264     /**
265      * Logs the duration for the magnification session which is activated by the triple tap and
266      * hold gesture.
267      *
268      * @param duration The duration of a triple-tap-and-hold activation session.
269      */
270     @VisibleForTesting
logMagnificationTripleTapAndHoldSession(long duration)271     void logMagnificationTripleTapAndHoldSession(long duration) {
272         AccessibilityStatsLogUtils.logMagnificationTripleTapAndHoldSession(duration);
273     }
274 
resetToDetectState()275     void resetToDetectState() {
276         transitionTo(mDetectingState);
277     }
278 
279     /**
280      * An interface to intercept the {@link MotionEvent} for gesture detection. The intercepted
281      * events should be delivered to next {@link EventStreamTransformation} with {
282      * {@link EventStreamTransformation#onMotionEvent(MotionEvent, MotionEvent, int)}} if there is
283      * no valid gestures.
284      */
285     interface State {
onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)286         void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
287 
clear()288         default void clear() {
289         }
290 
onEnter()291         default void onEnter() {
292         }
293 
onExit()294         default void onExit() {
295         }
296 
name()297         default String name() {
298             return getClass().getSimpleName();
299         }
300 
nameOf(@ullable State s)301         static String nameOf(@Nullable State s) {
302             return s != null ? s.name() : "null";
303         }
304     }
305 
transitionTo(State state)306     private void transitionTo(State state) {
307         if (DEBUG_STATE_TRANSITIONS) {
308             Slog.i(mLogTag, "state transition: " + (State.nameOf(mCurrentState) + " -> "
309                     + State.nameOf(state) + " at "
310                     + asList(copyOfRange(new RuntimeException().getStackTrace(), 1, 5)))
311                     .replace(getClass().getName(), ""));
312         }
313         mPreviousState = mCurrentState;
314         if (mPreviousState != null) {
315             mPreviousState.onExit();
316         }
317         mCurrentState = state;
318         if (mCurrentState != null) {
319             mCurrentState.onEnter();
320         }
321     }
322 
323     /**
324      * When entering this state, {@link PanningScalingHandler} will be enabled to address the
325      * gestures until receiving {@link MotionEvent#ACTION_UP} or {@link MotionEvent#ACTION_CANCEL}.
326      * When leaving this state, current scale will be persisted.
327      */
328     final class PanningScalingGestureState implements State {
329         private final PanningScalingHandler mPanningScalingHandler;
330 
PanningScalingGestureState(PanningScalingHandler panningScalingHandler)331         PanningScalingGestureState(PanningScalingHandler panningScalingHandler) {
332             mPanningScalingHandler = panningScalingHandler;
333         }
334 
335         @Override
onEnter()336         public void onEnter() {
337             mPanningScalingHandler.setEnabled(true);
338         }
339 
340         @Override
onExit()341         public void onExit() {
342             mPanningScalingHandler.setEnabled(false);
343             mMagnificationConnectionManager.persistScale(mDisplayId);
344             clear();
345         }
346 
347         @Override
onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)348         public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
349             int action = event.getActionMasked();
350             if (action == ACTION_UP || action == ACTION_CANCEL) {
351                 transitionTo(mDetectingState);
352             }
353         }
354 
355         @Override
clear()356         public void clear() {
357             mPanningScalingHandler.clear();
358         }
359 
360         @Override
toString()361         public String toString() {
362             return "PanningScalingState{"
363                     + "mPanningScalingHandler=" + mPanningScalingHandler + '}';
364         }
365     }
366 
367     /**
368      * A state not to intercept {@link MotionEvent}. Leaving this state until receiving
369      * {@link MotionEvent#ACTION_UP} or {@link MotionEvent#ACTION_CANCEL}.
370      */
371     final class DelegatingState implements State {
372         private final MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
373 
DelegatingState(MotionEventDispatcherDelegate motionEventDispatcherDelegate)374         DelegatingState(MotionEventDispatcherDelegate motionEventDispatcherDelegate) {
375             mMotionEventDispatcherDelegate = motionEventDispatcherDelegate;
376         }
377 
378         @Override
onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)379         public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
380             mMotionEventDispatcherDelegate.dispatchMotionEvent(event, rawEvent, policyFlags);
381             switch (event.getActionMasked()) {
382                 case ACTION_UP:
383                 case ACTION_CANCEL: {
384                     transitionTo(mDetectingState);
385                 }
386                     break;
387             }
388         }
389     }
390 
391 
392     /**
393      * This class handles motion events when the event dispatcher has
394      * determined that the user is performing a single-finger drag of the
395      * magnification viewport.
396      *
397      * Leaving this state until receiving {@link MotionEvent#ACTION_UP}
398      * or {@link MotionEvent#ACTION_CANCEL}.
399      */
400     final class ViewportDraggingState implements State {
401 
402         /** Whether to disable zoom after dragging ends */
403         boolean mEnabledBeforeDrag;
404 
405         private float mLastX = Float.NaN;
406         private float mLastY = Float.NaN;
407 
408         @Override
onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)409         public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
410             final int action = event.getActionMasked();
411             switch (action) {
412                 case ACTION_MOVE: {
413                     if (!Float.isNaN(mLastX) && !Float.isNaN(mLastY)) {
414                         float offsetX = event.getX() - mLastX;
415                         float offsetY = event.getY() - mLastY;
416                         mMagnificationConnectionManager.moveWindowMagnification(mDisplayId, offsetX,
417                                 offsetY);
418                     }
419                     mLastX = event.getX();
420                     mLastY = event.getY();
421                 }
422                 break;
423 
424                 case ACTION_UP:
425                 case ACTION_CANCEL: {
426                     releaseTripleTapAndHold();
427                 }
428                     break;
429             }
430         }
431 
432         @Override
clear()433         public void clear() {
434             mLastX = Float.NaN;
435             mLastY = Float.NaN;
436         }
437 
438         @Override
onExit()439         public void onExit() {
440             clear();
441         }
442 
443         @Override
toString()444         public String toString() {
445             return "ViewportDraggingState{"
446                     + "mLastX=" + mLastX
447                     + ",mLastY=" + mLastY
448                     + '}';
449         }
450     }
451 
452     /**
453      * This class handles motion events in a duration to determine if the user is going to
454      * manipulate the window magnifier or want to interact with current UI. The rule of leaving
455      * this state is as follows:
456      * <ol>
457      *   <li> If {@link MagnificationGestureMatcher#GESTURE_TWO_FINGERS_DOWN_OR_SWIPE} is detected,
458      *   {@link State} will be transited to {@link PanningScalingGestureState}.</li>
459      *   <li> If other gesture is detected and the last motion event is neither ACTION_UP nor
460      *   ACTION_CANCEL.
461      * </ol>
462      *  <b>Note</b> The motion events will be cached and dispatched before leaving this state.
463      */
464     final class DetectingState implements State,
465             MagnificationGesturesObserver.Callback {
466 
467         private final MagnificationGesturesObserver mGesturesObserver;
468 
DetectingState(@iContext Context context)469         DetectingState(@UiContext Context context) {
470             if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
471                 final List<GestureMatcher> mGestureMatchers = new ArrayList<>();
472 
473                 mGestureMatchers.add(new SimpleSwipe(context));
474                 // Observe single tap and single tap and hold to reduce response time when the
475                 // user performs these two gestures inside the window magnifier.
476                 mGestureMatchers.add(new MultiTap(context,
477                         mDetectSingleFingerTripleTap ? 3 : 1,
478                         mDetectSingleFingerTripleTap
479                                 ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
480                                 : MagnificationGestureMatcher.GESTURE_SINGLE_TAP,
481                         MagnificationGestureMatcher.getMagnificationMultiTapTimeout(mContext),
482                         null));
483                 mGestureMatchers.add(new MultiTapAndHold(context,
484                         mDetectSingleFingerTripleTap ? 3 : 1,
485                         mDetectSingleFingerTripleTap
486                                 ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
487                                 : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD,
488                         MagnificationGestureMatcher.getMagnificationMultiTapTimeout(mContext),
489                         null));
490                 mGestureMatchers.add(new TwoFingersDownOrSwipe(context));
491 
492                 if (mDetectTwoFingerTripleTap) {
493                     mGestureMatchers.add(new MultiFingerMultiTap(context, /* fingers= */ 2,
494                             /* taps= */ 2, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP,
495                             null));
496                     mGestureMatchers.add(new MultiFingerMultiTapAndHold(context, /* fingers= */ 2,
497                             /* taps= */ 2, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD,
498                             null));
499                 }
500 
501                 mGesturesObserver = new MagnificationGesturesObserver(this,
502                         mGestureMatchers.toArray(new GestureMatcher[mGestureMatchers.size()]));
503             } else {
504                 final MultiTap multiTap = new MultiTap(context,
505                         mDetectSingleFingerTripleTap ? 3 : 1,
506                         mDetectSingleFingerTripleTap
507                                 ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
508                                 : MagnificationGestureMatcher.GESTURE_SINGLE_TAP,
509                         MagnificationGestureMatcher.getMagnificationMultiTapTimeout(mContext),
510                         null);
511                 final MultiTapAndHold multiTapAndHold = new MultiTapAndHold(context,
512                         mDetectSingleFingerTripleTap ? 3 : 1,
513                         mDetectSingleFingerTripleTap
514                                 ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
515                                 : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD,
516                         MagnificationGestureMatcher.getMagnificationMultiTapTimeout(mContext),
517                         null);
518                 mGesturesObserver = new MagnificationGesturesObserver(this,
519                         new SimpleSwipe(context),
520                         multiTap,
521                         multiTapAndHold,
522                         new TwoFingersDownOrSwipe(context));
523             }
524         }
525 
526         @Override
onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)527         public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
528             mGesturesObserver.onMotionEvent(event, rawEvent, policyFlags);
529         }
530 
531         @Override
toString()532         public String toString() {
533             return "DetectingState{"
534                     + "mGestureTimeoutObserver=" + mGesturesObserver
535                     + '}';
536         }
537 
538         @Override
shouldStopDetection(MotionEvent motionEvent)539         public boolean shouldStopDetection(MotionEvent motionEvent) {
540             return !mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
541                     && !mDetectSingleFingerTripleTap
542                     && !(mDetectTwoFingerTripleTap
543                     && Flags.enableMagnificationMultipleFingerMultipleTapGesture());
544         }
545 
546         @Override
onGestureCompleted(int gestureId, long lastDownEventTime, List<MotionEventInfo> delayedEventQueue, MotionEvent motionEvent)547         public void onGestureCompleted(int gestureId, long lastDownEventTime,
548                 List<MotionEventInfo> delayedEventQueue,
549                 MotionEvent motionEvent) {
550             if (DEBUG_DETECTING) {
551                 Slog.d(mLogTag, "onGestureDetected : gesture = "
552                         + MagnificationGestureMatcher.gestureIdToString(
553                         gestureId));
554                 Slog.d(mLogTag,
555                         "onGestureDetected : delayedEventQueue = " + delayedEventQueue);
556             }
557             if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
558                     && mMagnificationConnectionManager
559                         .pointersInWindow(mDisplayId, motionEvent) > 0) {
560                 transitionTo(mObservePanningScalingState);
561             } else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
562                 onTripleTap(motionEvent);
563             } else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD) {
564                 onTripleTapAndHold(motionEvent);
565             } else {
566                 mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue,
567                         lastDownEventTime);
568                 changeToDelegateStateIfNeed(motionEvent);
569             }
570         }
571 
572         @Override
onGestureCancelled(long lastDownEventTime, List<MotionEventInfo> delayedEventQueue, MotionEvent motionEvent)573         public void onGestureCancelled(long lastDownEventTime,
574                 List<MotionEventInfo> delayedEventQueue,
575                 MotionEvent motionEvent) {
576             if (DEBUG_DETECTING) {
577                 Slog.d(mLogTag,
578                         "onGestureCancelled : delayedEventQueue = " + delayedEventQueue);
579             }
580             mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue,
581                     lastDownEventTime);
582             changeToDelegateStateIfNeed(motionEvent);
583         }
584 
changeToDelegateStateIfNeed(MotionEvent motionEvent)585         private void changeToDelegateStateIfNeed(MotionEvent motionEvent) {
586             if (motionEvent != null && (motionEvent.getActionMasked() == ACTION_UP
587                     || motionEvent.getActionMasked() == ACTION_CANCEL)) {
588                 return;
589             }
590             transitionTo(mDelegatingState);
591         }
592     }
593 
594     @Override
toString()595     public String toString() {
596         return "WindowMagnificationGestureHandler{"
597                 + "mDetectingState=" + mDetectingState
598                 + ", mDelegatingState=" + mDelegatingState
599                 + ", mViewportDraggingState=" + mViewportDraggingState
600                 + ", mMagnifiedInteractionState=" + mObservePanningScalingState
601                 + ", mCurrentState=" + State.nameOf(mCurrentState)
602                 + ", mPreviousState=" + State.nameOf(mPreviousState)
603                 + ", mMagnificationConnectionManager=" + mMagnificationConnectionManager
604                 + ", mDisplayId=" + mDisplayId
605                 + '}';
606     }
607 }
608