chore(#Magnification): use persisted scale if zoom-in-temporary when magnifier is zoomed out by service

Originally when the fullscreen magnifier is activated, using triple-tap-and-hold to temporarily zoom in would use (current scale + 1) as the furthur zoom in scale.

We add two cases that would zoom in with persisted scale.
1. User just tap the shortcut to activate the magnifier.
2. The magnifier just zooms out due to user context changed such as transitions or rotations.

For these two cases we conclude the expected bahaviour should be zooming in with persisted scale.

Bug: 289131167
Test: manually
      atest FullScreenMagnificationControllerTest
      atest FullScreenMagnificationGestureHandlerTest
Change-Id: I0a48e1465763c5387bbac537471de82efff7caf5
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 3f3fa34..d33d224 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -142,6 +142,8 @@
         private int mIdOfLastServiceToMagnify = INVALID_SERVICE_ID;
         private boolean mMagnificationActivated = false;
 
+        private boolean mZoomedOutFromService = false;
+
         @GuardedBy("mLock") @Nullable private MagnificationThumbnail mMagnificationThumbnail;
 
         DisplayMagnification(int displayId) {
@@ -545,6 +547,24 @@
             return changed;
         }
 
+        /**
+         * Directly Zooms out the scale to 1f with animating the transition. This method is
+         * triggered only by service automatically, such as when user context changed.
+         */
+        void zoomOutFromService() {
+            setScaleAndCenter(1.0f, Float.NaN, Float.NaN,
+                    transformToStubCallback(true),
+                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+            mZoomedOutFromService = true;
+        }
+
+        /**
+         * Whether the zooming out is triggered by {@link #zoomOutFromService}.
+         */
+        boolean isZoomedOutFromService() {
+            return mZoomedOutFromService;
+        }
+
         @GuardedBy("mLock")
         boolean reset(boolean animate) {
             return reset(transformToStubCallback(animate));
@@ -619,6 +639,8 @@
                             mIdOfLastServiceToMagnify);
                 });
             }
+            // the zoom scale would be changed so we reset the flag
+            mZoomedOutFromService = false;
             return changed;
         }
 
@@ -944,9 +966,7 @@
             }
 
             if (isAlwaysOnMagnificationEnabled()) {
-                setScaleAndCenter(displayId, 1.0f, Float.NaN, Float.NaN,
-                        true,
-                        AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+                zoomOutFromService(displayId);
             } else {
                 reset(displayId, true);
             }
@@ -1428,6 +1448,37 @@
     }
 
     /**
+     * Directly Zooms out the scale to 1f with animating the transition. This method is
+     * triggered only by service automatically, such as when user context changed.
+     *
+     * @param displayId The logical display id.
+     */
+    private void zoomOutFromService(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null || !display.isActivated()) {
+                return;
+            }
+            display.zoomOutFromService();
+        }
+    }
+
+    /**
+     * Whether the magnification is zoomed out by {@link #zoomOutFromService(int)}.
+     *
+     * @param displayId The logical display id.
+     */
+    public boolean isZoomedOutFromService(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null || !display.isActivated()) {
+                return false;
+            }
+            return display.isZoomedOutFromService();
+        }
+    }
+
+    /**
      * Resets all displays' magnification if last magnifying service is disabled.
      *
      * @param connectionId
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 4aebbf1..03ba295 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -1093,7 +1093,7 @@
 
             mViewportDraggingState.prepareForZoomInTemporary(shortcutTriggered);
 
-            zoomInTemporary(down.getX(), down.getY());
+            zoomInTemporary(down.getX(), down.getY(), shortcutTriggered);
 
             transitionTo(mViewportDraggingState);
         }
@@ -1150,14 +1150,20 @@
         }
     }
 
-    private void zoomInTemporary(float centerX, float centerY) {
+    private void zoomInTemporary(float centerX, float centerY, boolean shortcutTriggered) {
         final float currentScale = mFullScreenMagnificationController.getScale(mDisplayId);
         final float persistedScale = MathUtils.constrain(
                 mFullScreenMagnificationController.getPersistedScale(mDisplayId),
                 MIN_SCALE, MAX_SCALE);
 
         final boolean isActivated = mFullScreenMagnificationController.isActivated(mDisplayId);
-        final float scale = isActivated ? (currentScale + 1.0f) : persistedScale;
+        final boolean isShortcutTriggered = shortcutTriggered;
+        final boolean isZoomedOutFromService =
+                mFullScreenMagnificationController.isZoomedOutFromService(mDisplayId);
+
+        boolean zoomInWithPersistedScale =
+                !isActivated || isShortcutTriggered || isZoomedOutFromService;
+        final float scale = zoomInWithPersistedScale ?  persistedScale : (currentScale + 1.0f);
         zoomToScale(scale, centerX, centerY);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 5b5c8d4..1b02498 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -130,6 +130,8 @@
 
     public DisplayManagerInternal mDisplayManagerInternalMock = mock(DisplayManagerInternal.class);
 
+    private float mOriginalMagnificationPersistedScale;
+
     @Before
     public void setUp() {
         Looper looper = InstrumentationRegistry.getContext().getMainLooper();
@@ -143,6 +145,9 @@
         mResolver = new MockContentResolver();
         mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mMockContext.getContentResolver()).thenReturn(mResolver);
+        mOriginalMagnificationPersistedScale = Settings.Secure.getFloatForUser(mResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.0f,
+                CURRENT_USER_ID);
         Settings.Secure.putFloatForUser(mResolver,
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.0f,
                 CURRENT_USER_ID);
@@ -169,6 +174,10 @@
     @After
     public void tearDown() {
         mMessageCapturingHandler.removeAllMessages();
+        Settings.Secure.putFloatForUser(mResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+                mOriginalMagnificationPersistedScale,
+                CURRENT_USER_ID);
     }
 
 
@@ -1311,7 +1320,10 @@
         mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(false);
         mFullScreenMagnificationController.onUserContextChanged(DISPLAY_0);
 
+        // the magnifier should be deactivated.
         verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(DISPLAY_0), eq(false));
+        assertFalse(mFullScreenMagnificationController.isZoomedOutFromService(DISPLAY_0));
+
         verify(mMockThumbnail).setThumbnailBounds(
                 /* currentBounds= */ any(),
                 /* scale= */ anyFloat(),
@@ -1330,8 +1342,11 @@
         mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
         mFullScreenMagnificationController.onUserContextChanged(DISPLAY_0);
 
+        // the magnifier should be zoomed out and keep activated by service action.
         assertEquals(1.0f, mFullScreenMagnificationController.getScale(DISPLAY_0), 0);
         assertTrue(mFullScreenMagnificationController.isActivated(DISPLAY_0));
+        assertTrue(mFullScreenMagnificationController.isZoomedOutFromService(DISPLAY_0));
+
         verify(mMockThumbnail).setThumbnailBounds(
                 /* currentBounds= */ any(),
                 /* scale= */ anyFloat(),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 989aee0..9304b32 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -96,19 +96,29 @@
  *          NON_ACTIVATED_ZOOMED_TMP -> IDLE [label="release"]
  *          SHORTCUT_TRIGGERED -> IDLE [label="a11y\nbtn"]
  *          SHORTCUT_TRIGGERED -> ACTIVATED [label="tap"]
- *          SHORTCUT_TRIGGERED -> SHORTCUT_TRIGGERED_ZOOMED_TMP [label="hold"]
- *          SHORTCUT_TRIGGERED -> PANNING [label="2hold]
+ *          SHORTCUT_TRIGGERED -> ZOOMED_WITH_PERSISTED_SCALE_TMP [label="hold"]
+ *          SHORTCUT_TRIGGERED -> PANNING [label="2hold"]
+ *          ZOOMED_OUT_FROM_SERVICE -> IDLE [label="a11y\nbtn"]
+ *          ZOOMED_OUT_FROM_SERVICE -> ZOOMED_OUT_FROM_SERVICE_DOUBLE_TAP [label="2tap"]
+ *          ZOOMED_OUT_FROM_SERVICE -> PANNING [label="2hold"]
+ *          ZOOMED_OUT_FROM_SERVICE_DOUBLE_TAP -> IDLE [label="tap"]
+ *          ZOOMED_OUT_FROM_SERVICE_DOUBLE_TAP -> ZOOMED_OUT_FROM_SERVICE [label="timeout"]
+ *          ZOOMED_OUT_FROM_SERVICE_DOUBLE_TAP -> ZOOMED_WITH_PERSISTED_SCALE_TMP [label="hold"]
  *          if always-on enabled:
- *              SHORTCUT_TRIGGERED_ZOOMED_TMP -> ACTIVATED [label="release"]
+ *              ZOOMED_WITH_PERSISTED_SCALE_TMP -> ACTIVATED [label="release"]
  *          else:
- *              SHORTCUT_TRIGGERED_ZOOMED_TMP -> IDLE [label="release"]
+ *              ZOOMED_WITH_PERSISTED_SCALE_TMP -> IDLE [label="release"]
  *          ACTIVATED -> ACTIVATED_DOUBLE_TAP [label="2tap"]
  *          ACTIVATED -> IDLE [label="a11y\nbtn"]
  *          ACTIVATED -> PANNING [label="2hold"]
+ *          if always-on enabled:
+ *              ACTIVATED -> ZOOMED_OUT_FROM_SERVICE [label="contextChanged"]
+ *          else:
+ *              ACTIVATED -> IDLE [label="contextChanged"]
  *          ACTIVATED_DOUBLE_TAP -> ACTIVATED [label="timeout"]
- *          ACTIVATED_DOUBLE_TAP -> ACTIVATED_ZOOMED_TMP [label="hold"]
+ *          ACTIVATED_DOUBLE_TAP -> ZOOMED_FURTHER_TMP [label="hold"]
  *          ACTIVATED_DOUBLE_TAP -> IDLE [label="tap"]
- *          ACTIVATED_ZOOMED_TMP -> ACTIVATED [label="release"]
+ *          ZOOMED_FURTHER_TMP -> ACTIVATED [label="release"]
  *          PANNING -> ACTIVATED [label="release"]
  *          PANNING -> PANNING_SCALING [label="pinch"]
  *          PANNING_SCALING -> ACTIVATED [label="release"]
@@ -120,15 +130,20 @@
 
     public static final int STATE_IDLE = 1;
     public static final int STATE_ACTIVATED = 2;
-    public static final int STATE_2TAPS = 3;
-    public static final int STATE_ACTIVATED_2TAPS = 4;
-    public static final int STATE_SHORTCUT_TRIGGERED = 5;
-    public static final int STATE_NON_ACTIVATED_ZOOMED_TMP = 6;
-    public static final int STATE_ACTIVATED_ZOOMED_TMP = 7;
-    public static final int STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP = 8;
-    public static final int STATE_PANNING = 9;
-    public static final int STATE_SCALING_AND_PANNING = 10;
-    public static final int STATE_SINGLE_PANNING = 11;
+    public static final int STATE_SHORTCUT_TRIGGERED = 3;
+    public static final int STATE_ZOOMED_OUT_FROM_SERVICE = 4;
+
+    public static final int STATE_2TAPS = 5;
+    public static final int STATE_ACTIVATED_2TAPS = 6;
+    public static final int STATE_ZOOMED_OUT_FROM_SERVICE_2TAPS = 7;
+
+    public static final int STATE_NON_ACTIVATED_ZOOMED_TMP = 8;
+    public static final int STATE_ZOOMED_FURTHER_TMP = 9;
+    public static final int STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP = 10;
+
+    public static final int STATE_PANNING = 11;
+    public static final int STATE_SCALING_AND_PANNING = 12;
+    public static final int STATE_SINGLE_PANNING = 13;
 
     public static final int FIRST_STATE = STATE_IDLE;
     public static final int LAST_STATE = STATE_SINGLE_PANNING;
@@ -313,7 +328,7 @@
         assertTransition(STATE_SHORTCUT_TRIGGERED, () -> {
             send(downEvent());
             fastForward1sec();
-        }, STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP);
+        }, STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP);
 
         // A11y button followed by a tap turns magnifier on
         assertTransition(STATE_SHORTCUT_TRIGGERED, () -> tap(), STATE_ACTIVATED);
@@ -337,22 +352,22 @@
         assertTransition(STATE_2TAPS, () -> swipeAndHold(), STATE_NON_ACTIVATED_ZOOMED_TMP);
 
         // release when activated temporary zoom in back to activated
-        assertTransition(STATE_ACTIVATED_ZOOMED_TMP, () -> send(upEvent()), STATE_ACTIVATED);
+        assertTransition(STATE_ZOOMED_FURTHER_TMP, () -> send(upEvent()), STATE_ACTIVATED);
     }
 
     @Test
-    public void testRelease_shortcutTriggeredZoomedTmp_alwaysOnNotEnabled_shouldInIdle() {
+    public void testRelease_zoomedWithPersistedScaleTmpAndAlwaysOnNotEnabled_shouldInIdle() {
         mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(false);
-        goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP);
+        goFromStateIdleTo(STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP);
         send(upEvent());
 
         assertIn(STATE_IDLE);
     }
 
     @Test
-    public void testRelease_shortcutTriggeredZoomedTmp_alwaysOnEnabled_shouldInActivated() {
+    public void testRelease_zoomedWithPersistedScaleTmpAndAlwaysOnEnabled_shouldInActivated() {
         mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
-        goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP);
+        goFromStateIdleTo(STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP);
         send(upEvent());
 
         assertIn(STATE_ACTIVATED);
@@ -404,9 +419,11 @@
     @Test
     public void testTripleTapAndHold_zoomsImmediately() {
         assertZoomsImmediatelyOnSwipeFrom(STATE_2TAPS, STATE_NON_ACTIVATED_ZOOMED_TMP);
+        assertZoomsImmediatelyOnSwipeFrom(STATE_ACTIVATED_2TAPS, STATE_ZOOMED_FURTHER_TMP);
         assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED,
-                STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP);
-        assertZoomsImmediatelyOnSwipeFrom(STATE_ACTIVATED_2TAPS, STATE_ACTIVATED_ZOOMED_TMP);
+                STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP);
+        assertZoomsImmediatelyOnSwipeFrom(STATE_ZOOMED_OUT_FROM_SERVICE_2TAPS,
+                STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP);
     }
 
     @Test
@@ -755,6 +772,10 @@
         mHandler.timeAdvance();
     }
 
+    private void triggerContextChanged() {
+        mFullScreenMagnificationController.onUserContextChanged(DISPLAY_0);
+    }
+
     /**
      * Asserts that {@link #mMgh the handler} is in the given {@code state}
      */
@@ -763,43 +784,63 @@
 
             // Asserts on separate lines for accurate stack traces
 
-            case STATE_IDLE: {
+            case STATE_IDLE:
                 check(tapCount() < 2, state);
                 check(!mMgh.mDetectingState.mShortcutTriggered, state);
                 check(!isActivated(), state);
                 check(!isZoomed(), state);
-            } break;
-            case STATE_ACTIVATED: {
+                break;
+            case STATE_ACTIVATED:
                 check(isActivated(), state);
                 check(tapCount() < 2, state);
-            } break;
-            case STATE_2TAPS: {
+                break;
+            case STATE_SHORTCUT_TRIGGERED:
+                check(mMgh.mDetectingState.mShortcutTriggered, state);
+                check(isActivated(), state);
+                check(!isZoomed(), state);
+                break;
+            case STATE_ZOOMED_OUT_FROM_SERVICE:
+                // the always-on feature must be enabled then this state is reachable.
+                assertTrue(mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled());
+                check(isActivated(), state);
+                check(!isZoomed(), state);
+                check(mMgh.mFullScreenMagnificationController.isZoomedOutFromService(DISPLAY_0),
+                        state);
+                break;
+            case STATE_2TAPS:
                 check(!isActivated(), state);
                 check(!isZoomed(), state);
                 check(tapCount() == 2, state);
-            } break;
-            case STATE_ACTIVATED_2TAPS: {
+                break;
+            case STATE_ACTIVATED_2TAPS:
                 check(isActivated(), state);
                 check(isZoomed(), state);
                 check(tapCount() == 2, state);
-            } break;
-            case STATE_NON_ACTIVATED_ZOOMED_TMP: {
+                break;
+            case STATE_ZOOMED_OUT_FROM_SERVICE_2TAPS:
+                check(isActivated(), state);
+                check(!isZoomed(), state);
+                check(mMgh.mFullScreenMagnificationController.isZoomedOutFromService(DISPLAY_0),
+                        state);
+                check(tapCount() == 2, state);
+                break;
+            case STATE_NON_ACTIVATED_ZOOMED_TMP:
                 check(isActivated(), state);
                 check(isZoomed(), state);
                 check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
                         state);
                 check(Float.isNaN(mMgh.mViewportDraggingState.mScaleToRecoverAfterDraggingEnd),
                         state);
-            } break;
-            case STATE_ACTIVATED_ZOOMED_TMP: {
+                break;
+            case STATE_ZOOMED_FURTHER_TMP:
                 check(isActivated(), state);
                 check(isZoomed(), state);
                 check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
                         state);
                 check(mMgh.mViewportDraggingState.mScaleToRecoverAfterDraggingEnd >= 1.0f,
                         state);
-            } break;
-            case STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP: {
+                break;
+            case STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP:
                 check(isActivated(), state);
                 check(isZoomed(), state);
                 check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
@@ -811,29 +852,25 @@
                     check(Float.isNaN(mMgh.mViewportDraggingState.mScaleToRecoverAfterDraggingEnd),
                             state);
                 }
-            } break;
-            case STATE_SHORTCUT_TRIGGERED: {
-                check(mMgh.mDetectingState.mShortcutTriggered, state);
-                check(isActivated(), state);
-                check(!isZoomed(), state);
-            } break;
-            case STATE_PANNING: {
+                break;
+            case STATE_PANNING:
                 check(isActivated(), state);
                 check(mMgh.mCurrentState == mMgh.mPanningScalingState,
                         state);
                 check(!mMgh.mPanningScalingState.mScaling, state);
-            } break;
-            case STATE_SCALING_AND_PANNING: {
+                break;
+            case STATE_SCALING_AND_PANNING:
                 check(isActivated(), state);
                 check(mMgh.mCurrentState == mMgh.mPanningScalingState,
                         state);
                 check(mMgh.mPanningScalingState.mScaling, state);
-            } break;
-            case STATE_SINGLE_PANNING: {
+                break;
+            case STATE_SINGLE_PANNING:
                 check(isZoomed(), state);
                 check(mMgh.mCurrentState == mMgh.mSinglePanningState, state);
-            } break;
-            default: throw new IllegalArgumentException("Illegal state: " + state);
+                break;
+            default:
+                throw new IllegalArgumentException("Illegal state: " + state);
         }
     }
 
@@ -843,15 +880,10 @@
     private void goFromStateIdleTo(int state) {
         try {
             switch (state) {
-                case STATE_IDLE: {
+                case STATE_IDLE:
                     mMgh.clearAndTransitionToStateDetecting();
-                } break;
-                case STATE_2TAPS: {
-                    goFromStateIdleTo(STATE_IDLE);
-                    tap();
-                    tap();
-                } break;
-                case STATE_ACTIVATED: {
+                    break;
+                case STATE_ACTIVATED:
                     if (mMgh.mDetectTripleTap) {
                         goFromStateIdleTo(STATE_2TAPS);
                         tap();
@@ -859,47 +891,63 @@
                         goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
                         tap();
                     }
-                } break;
-                case STATE_ACTIVATED_2TAPS: {
+                    break;
+                case STATE_SHORTCUT_TRIGGERED:
+                    goFromStateIdleTo(STATE_IDLE);
+                    triggerShortcut();
+                    break;
+                case STATE_ZOOMED_OUT_FROM_SERVICE:
+                    // the always-on feature must be enabled then this state is reachable.
+                    assertTrue(mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled());
+                    goFromStateIdleTo(STATE_ACTIVATED);
+                    triggerContextChanged();
+                    break;
+                case STATE_2TAPS:
+                    goFromStateIdleTo(STATE_IDLE);
+                    tap();
+                    tap();
+                    break;
+                case STATE_ACTIVATED_2TAPS:
                     goFromStateIdleTo(STATE_ACTIVATED);
                     tap();
                     tap();
-                } break;
-                case STATE_NON_ACTIVATED_ZOOMED_TMP: {
+                    break;
+                case STATE_ZOOMED_OUT_FROM_SERVICE_2TAPS:
+                    goFromStateIdleTo(STATE_ZOOMED_OUT_FROM_SERVICE);
+                    tap();
+                    tap();
+                    break;
+                case STATE_NON_ACTIVATED_ZOOMED_TMP:
                     goFromStateIdleTo(STATE_2TAPS);
                     send(downEvent());
                     fastForward1sec();
-                } break;
-                case STATE_ACTIVATED_ZOOMED_TMP: {
+                    break;
+                case STATE_ZOOMED_FURTHER_TMP:
                     goFromStateIdleTo(STATE_ACTIVATED_2TAPS);
                     send(downEvent());
                     fastForward1sec();
-                } break;
-                case STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP: {
+                    break;
+                case STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP:
                     goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
                     send(downEvent());
                     fastForward1sec();
-                } break;
-                case STATE_SHORTCUT_TRIGGERED: {
-                    goFromStateIdleTo(STATE_IDLE);
-                    triggerShortcut();
-                } break;
-                case STATE_PANNING: {
+                    break;
+                case STATE_PANNING:
                     goFromStateIdleTo(STATE_ACTIVATED);
                     send(downEvent());
                     send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
                     fastForward(ViewConfiguration.getTapTimeout());
-                } break;
-                case STATE_SCALING_AND_PANNING: {
+                    break;
+                case STATE_SCALING_AND_PANNING:
                     goFromStateIdleTo(STATE_PANNING);
                     send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 3));
                     send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 4));
                     send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 5));
-                } break;
-                case STATE_SINGLE_PANNING: {
+                    break;
+                case STATE_SINGLE_PANNING:
                     goFromStateIdleTo(STATE_ACTIVATED);
                     swipeAndHold();
-                } break;
+                    break;
                 default:
                     throw new IllegalArgumentException("Illegal state: " + state);
             }
@@ -913,14 +961,11 @@
      */
     private void returnToNormalFrom(int state) {
         switch (state) {
-            case STATE_IDLE: {
+            case STATE_IDLE:
                 // no op
-            } break;
-            case STATE_2TAPS: {
-                allowEventDelegation();
-                fastForward1sec();
-            } break;
-            case STATE_ACTIVATED: {
+                break;
+            case STATE_ACTIVATED:
+            case STATE_ZOOMED_OUT_FROM_SERVICE:
                 if (mMgh.mDetectTripleTap) {
                     tap();
                     tap();
@@ -928,39 +973,45 @@
                 } else {
                     triggerShortcut();
                 }
-            } break;
-            case STATE_ACTIVATED_2TAPS: {
+                break;
+            case STATE_SHORTCUT_TRIGGERED:
+                triggerShortcut();
+                break;
+            case STATE_2TAPS:
+                allowEventDelegation();
+                fastForward1sec();
+                break;
+            case STATE_ACTIVATED_2TAPS:
+            case STATE_ZOOMED_OUT_FROM_SERVICE_2TAPS:
                 tap();
-            } break;
-            case STATE_NON_ACTIVATED_ZOOMED_TMP: {
+                break;
+            case STATE_NON_ACTIVATED_ZOOMED_TMP:
                 send(upEvent());
-            } break;
-            case STATE_ACTIVATED_ZOOMED_TMP: {
+                break;
+            case STATE_ZOOMED_FURTHER_TMP:
                 send(upEvent());
                 returnToNormalFrom(STATE_ACTIVATED);
-            } break;
-            case STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP: {
+                break;
+            case STATE_ZOOMED_WITH_PERSISTED_SCALE_TMP:
                 send(upEvent());
                 if (mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled()) {
                     returnToNormalFrom(STATE_ACTIVATED);
                 }
-            } break;
-            case STATE_SHORTCUT_TRIGGERED: {
-                triggerShortcut();
-            } break;
-            case STATE_PANNING: {
+                break;
+            case STATE_PANNING:
                 send(pointerEvent(ACTION_POINTER_UP, DEFAULT_X * 2, DEFAULT_Y));
                 send(upEvent());
                 returnToNormalFrom(STATE_ACTIVATED);
-            } break;
-            case STATE_SCALING_AND_PANNING: {
+                break;
+            case STATE_SCALING_AND_PANNING:
                 returnToNormalFrom(STATE_PANNING);
-            } break;
-            case STATE_SINGLE_PANNING: {
+                break;
+            case STATE_SINGLE_PANNING:
                 send(upEvent());
                 returnToNormalFrom(STATE_ACTIVATED);
-            } break;
-            default: throw new IllegalArgumentException("Illegal state: " + state);
+                break;
+            default:
+                throw new IllegalArgumentException("Illegal state: " + state);
         }
     }