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

Commit 1d1ea541 authored by Jason Chang's avatar Jason Chang Committed by Bill Lin
Browse files

(1/n) Disabling OHM gesture in NavigationBar 3 Button mode

1) UX mentioned the swipe gesture in 3-Button NavgationBar area
may confuse user from taping NavBar buttons(Back, Home, Recents).
They decide to disable the swipe gesture and allow user to trigger
One Handed Mode(3 Button Mode) through A11y shortcut.
However, user can still use OHM by enabled shortcut button on
both Gesture Navigation & 3-Button mode.

2) OHM gestural handler consume the touch event while window
Magnification overlap with NavigationBar area in 3-button mode,
this result Magnification function can no long obtain the control.

Bug: 184903678
Bug: 179648683

Test: manual
Test: atest WMShellUnitTests
Change-Id: Ib9a06e19d749707e6f5e08c3ef803c1681f86bbc
parent a0e43e71
Loading
Loading
Loading
Loading
+0 −12
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.wm.shell.onehanded;
import android.content.res.Configuration;

import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;

/**
 * Interface to engage one handed feature.
@@ -59,11 +58,6 @@ public interface OneHanded {
     */
    void stopOneHanded(int uiEvent);

    /**
     * Sets navigation 3 button mode enabled or disabled by users.
     */
    void setThreeButtonModeEnabled(boolean enabled);

    /**
     * Sets one handed feature temporary locked in enabled or disabled state, this won't change
     * settings configuration.
@@ -79,12 +73,6 @@ public interface OneHanded {
     */
    void registerTransitionCallback(OneHandedTransitionCallback callback);

    /**
     * Registers callback for one handed gesture, this gesture callback will be activated on
     * 3 button navigation mode only
     */
    void registerGestureCallback(OneHandedGestureEventCallback callback);

    /**
     * Receive onConfigurationChanged() events
     */
+2 −42
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Slog;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;

@@ -59,7 +58,6 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;

import java.io.PrintWriter;

@@ -101,7 +99,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
    private final OneHandedImpl mImpl = new OneHandedImpl();

    private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
    private OneHandedGestureHandler mGestureHandler;
    private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;

    /**
@@ -113,7 +110,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
                    return;
                }
                mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct);
                mGestureHandler.onRotateDisplay(mDisplayAreaOrganizer.getDisplayLayout());
            };

    private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
@@ -196,7 +192,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>

    private boolean isInitialized() {
        if (mDisplayAreaOrganizer == null || mDisplayController == null
                || mGestureHandler == null || mOneHandedSettingsUtil == null) {
                || mOneHandedSettingsUtil == null) {
            Slog.w(TAG, "Components may not initialized yet!");
            return false;
        }
@@ -226,8 +222,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
                new OneHandedAnimationController(context);
        OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
                mainExecutor);
        OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler(
                context, displayLayout, ViewConfiguration.get(context), mainExecutor);
        OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
                new OneHandedBackgroundPanelOrganizer(context, displayLayout, mainExecutor);
        OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
@@ -238,7 +232,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
                ServiceManager.getService(Context.OVERLAY_SERVICE));
        return new OneHandedController(context, displayController,
                oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
                gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler, transitionState,
                settingsUtil, accessibilityUtil, timeoutHandler, transitionState,
                oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
                mainHandler);
    }
@@ -250,7 +244,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
            OneHandedDisplayAreaOrganizer displayAreaOrganizer,
            OneHandedTouchHandler touchHandler,
            OneHandedTutorialHandler tutorialHandler,
            OneHandedGestureHandler gestureHandler,
            OneHandedSettingsUtil settingsUtil,
            OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
            OneHandedTimeoutHandler timeoutHandler,
@@ -269,7 +262,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
        mTouchHandler = touchHandler;
        mState = state;
        mTutorialHandler = tutorialHandler;
        mGestureHandler = gestureHandler;
        mOverlayManager = overlayManager;
        mMainExecutor = mainExecutor;
        mMainHandler = mainHandler;
@@ -399,24 +391,15 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
        mOneHandedUiEventLogger.writeEvent(uiEvent);
    }

    private void setThreeButtonModeEnabled(boolean enabled) {
        mGestureHandler.onThreeButtonModeEnabled(enabled);
    }

    @VisibleForTesting
    void registerTransitionCallback(OneHandedTransitionCallback callback) {
        mDisplayAreaOrganizer.registerTransitionCallback(callback);
    }

    private void registerGestureCallback(OneHandedGestureEventCallback callback) {
        mGestureHandler.setGestureEventListener(callback);
    }

    private void setupCallback() {
        mTouchHandler.registerTouchEventListener(() ->
                stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT));
        mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
        mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
        mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
        mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer);
        mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallBack);
@@ -465,7 +448,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
    private void updateDisplayLayout(int displayId) {
        final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId);
        mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
        mGestureHandler.onDisplayChanged(newDisplayLayout);
        mTutorialHandler.onDisplayChanged(newDisplayLayout);
    }

@@ -588,7 +570,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
        }

        mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
        mGestureHandler.onGestureEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);

        if (!mIsOneHandedEnabled) {
            mDisplayAreaOrganizer.unregisterOrganizer();
@@ -643,9 +624,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
            return;
        }
        mLockedDisabled = locked && !enabled;

        // Disabled gesture when keyguard ON
        mGestureHandler.onGestureEnabled(!mLockedDisabled && isFeatureEnabled);
    }

    private void onConfigChanged(Configuration newConfig) {
@@ -685,10 +663,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
            mDisplayAreaOrganizer.dump(pw);
        }

        if (mGestureHandler != null) {
            mGestureHandler.dump(pw);
        }

        if (mTouchHandler != null) {
            mTouchHandler.dump(pw);
        }
@@ -774,13 +748,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
            });
        }

        @Override
        public void setThreeButtonModeEnabled(boolean enabled) {
            mMainExecutor.execute(() -> {
                OneHandedController.this.setThreeButtonModeEnabled(enabled);
            });
        }

        @Override
        public void setLockedDisabled(boolean locked, boolean enabled) {
            mMainExecutor.execute(() -> {
@@ -795,13 +762,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
            });
        }

        @Override
        public void registerGestureCallback(OneHandedGestureEventCallback callback) {
            mMainExecutor.execute(() -> {
                OneHandedController.this.registerGestureCallback(callback);
            });
        }

        @Override
        public void onConfigChanged(Configuration newConfig) {
            mMainExecutor.execute(() -> {
+0 −308
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.wm.shell.onehanded;

import static android.view.Display.DEFAULT_DISPLAY;

import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Looper;
import android.view.Display;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputMonitor;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewConfiguration;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;

import java.io.PrintWriter;

/**
 * The class manage swipe up and down gesture for 3-Button mode navigation, others(e.g, 2-button,
 * full gesture mode) are handled by Launcher quick steps. TODO(b/160934654) Migrate to Launcher
 * quick steps
 */
public class OneHandedGestureHandler implements OneHandedTransitionCallback {
    private static final String TAG = "OneHandedGestureHandler";

    private static final int ANGLE_MAX = 150;
    private static final int ANGLE_MIN = 30;
    private final float mDragDistThreshold;
    private final float mSquaredSlop;
    private final PointF mDownPos = new PointF();
    private final PointF mLastPos = new PointF();
    private final PointF mStartDragPos = new PointF();

    private boolean mPassedSlop;
    private boolean mAllowGesture;
    private boolean mIsEnabled;
    private int mNavGestureHeight;
    private boolean mIsThreeButtonModeEnabled;
    private int mRotation = Surface.ROTATION_0;

    @VisibleForTesting
    InputMonitor mInputMonitor;
    @VisibleForTesting
    InputEventReceiver mInputEventReceiver;
    private final ShellExecutor mMainExecutor;
    @VisibleForTesting
    @Nullable
    OneHandedGestureEventCallback mGestureEventCallback;
    private Rect mGestureRegion = new Rect();
    private boolean mIsStopGesture;

    /**
     * Constructor of OneHandedGestureHandler, we only handle the gesture of {@link
     * Display#DEFAULT_DISPLAY}
     *
     * @param context       Any context
     * @param displayLayout Current {@link DisplayLayout} from controller
     * @param viewConfig    {@link ViewConfiguration} to obtain touch slop
     * @param mainExecutor  The wm-shell main executor
     */
    public OneHandedGestureHandler(Context context,
            DisplayLayout displayLayout,
            ViewConfiguration viewConfig,
            ShellExecutor mainExecutor) {
        mMainExecutor = mainExecutor;
        mDragDistThreshold = context.getResources().getDimensionPixelSize(
                R.dimen.gestures_onehanded_drag_threshold);

        final float slop = viewConfig.getScaledTouchSlop();
        mSquaredSlop = slop * slop;
        onDisplayChanged(displayLayout);
        updateIsEnabled();
    }

    /**
     * Notifies by {@link OneHandedController}, when swipe down gesture is enabled on 3 button
     * navigation bar mode.
     *
     * @param isEnabled Either one handed mode or swipe for notification function enabled or not
     */
    public void onGestureEnabled(boolean isEnabled) {
        mIsEnabled = isEnabled;
        updateIsEnabled();
    }

    void onThreeButtonModeEnabled(boolean isEnabled) {
        mIsThreeButtonModeEnabled = isEnabled;
        updateIsEnabled();
    }

    /**
     * Registers {@link OneHandedGestureEventCallback} to receive onStart(), onStop() callback
     */
    public void setGestureEventListener(OneHandedGestureEventCallback callback) {
        mGestureEventCallback = callback;
    }

    /**
     * Called when onDisplayAdded() or onDisplayRemoved() callback
     * @param displayLayout The latest {@link DisplayLayout} representing current displayId
     */
    public void onDisplayChanged(DisplayLayout displayLayout) {
        mNavGestureHeight = getNavBarSize(displayLayout);
        mGestureRegion.set(0, displayLayout.height() - mNavGestureHeight, displayLayout.width(),
                displayLayout.height());
        mRotation = displayLayout.rotation();
    }

    private void onMotionEvent(MotionEvent ev) {
        int action = ev.getActionMasked();
        if (action == MotionEvent.ACTION_DOWN) {
            mAllowGesture = isWithinTouchRegion(ev.getX(), ev.getY()) && isGestureAvailable();
            if (mAllowGesture) {
                mDownPos.set(ev.getX(), ev.getY());
                mLastPos.set(mDownPos);
            }
        } else if (mAllowGesture) {
            switch (action) {
                case MotionEvent.ACTION_MOVE:
                    mLastPos.set(ev.getX(), ev.getY());
                    if (!mPassedSlop) {
                        if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
                                > mSquaredSlop) {
                            mStartDragPos.set(mLastPos.x, mLastPos.y);
                            if (isValidStartAngle(
                                    mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)
                                    || isValidExitAngle(
                                    mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)) {
                                mPassedSlop = true;
                                mInputMonitor.pilferPointers();
                            }
                        }
                    } else {
                        float distance = (float) Math.hypot(mLastPos.x - mDownPos.x,
                                mLastPos.y - mDownPos.y);
                        if (distance > mDragDistThreshold) {
                            mIsStopGesture = true;
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (mLastPos.y >= mDownPos.y && mPassedSlop) {
                        mGestureEventCallback.onStart();
                    } else if (mIsStopGesture) {
                        mGestureEventCallback.onStop();
                    }
                    clearState();
                    break;
                case MotionEvent.ACTION_CANCEL:
                    clearState();
                    break;
                default:
                    break;
            }
        }
    }

    private void clearState() {
        mPassedSlop = false;
        mIsStopGesture = false;
    }

    private void disposeInputChannel() {
        if (mInputEventReceiver != null) {
            mInputEventReceiver.dispose();
            mInputEventReceiver = null;
        }

        if (mInputMonitor != null) {
            mInputMonitor.dispose();
            mInputMonitor = null;
        }
    }

    private boolean isWithinTouchRegion(float x, float y) {
        return mGestureRegion.contains(Math.round(x), Math.round(y));
    }

    private int getNavBarSize(@NonNull DisplayLayout displayLayout) {
        return isGestureAvailable() ? displayLayout.navBarFrameHeight() : 0 /* In landscape */;
    }

    private void updateIsEnabled() {
        disposeInputChannel();

        if (mIsEnabled && mIsThreeButtonModeEnabled && isGestureAvailable()) {
            mInputMonitor = InputManager.getInstance().monitorGestureInput(
                    "onehanded-gesture-offset", DEFAULT_DISPLAY);
            try {
                mMainExecutor.executeBlocking(() -> {
                    mInputEventReceiver = new EventReceiver(
                            mInputMonitor.getInputChannel(), Looper.myLooper());
                });
            } catch (InterruptedException e) {
                throw new RuntimeException("Failed to create input event receiver", e);
            }
        }
    }

    private void onInputEvent(InputEvent ev) {
        if (ev instanceof MotionEvent) {
            onMotionEvent((MotionEvent) ev);
        }
    }

    /**
     * Handler for display rotation changes by {@link DisplayLayout}
     *
     * @param displayLayout The rotated displayLayout
     */
    public void onRotateDisplay(DisplayLayout displayLayout) {
        mRotation = displayLayout.rotation();
        mNavGestureHeight = getNavBarSize(displayLayout);
        mGestureRegion.set(0, displayLayout.height() - mNavGestureHeight, displayLayout.width(),
                displayLayout.height());
        updateIsEnabled();
    }

    // TODO: Use BatchedInputEventReceiver
    private class EventReceiver extends InputEventReceiver {
        EventReceiver(InputChannel channel, Looper looper) {
            super(channel, looper);
        }

        public void onInputEvent(InputEvent event) {
            OneHandedGestureHandler.this.onInputEvent(event);
            finishInputEvent(event, true);
        }
    }

    private boolean isGestureAvailable() {
        // Either OHM or swipe notification shade can activate in portrait mode only
        return mRotation == Surface.ROTATION_0 || mRotation == Surface.ROTATION_180;
    }

    private boolean isValidStartAngle(float deltaX, float deltaY) {
        final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
        return angle > -(ANGLE_MAX) && angle < -(ANGLE_MIN);
    }

    private boolean isValidExitAngle(float deltaX, float deltaY) {
        final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
        return angle > ANGLE_MIN && angle < ANGLE_MAX;
    }

    private float squaredHypot(float x, float y) {
        return x * x + y * y;
    }

    void dump(@NonNull PrintWriter pw) {
        final String innerPrefix = "  ";
        pw.println(TAG);
        pw.print(innerPrefix + "mAllowGesture=");
        pw.println(mAllowGesture);
        pw.print(innerPrefix + "mIsEnabled=");
        pw.println(mIsEnabled);
        pw.print(innerPrefix + "mGestureRegion=");
        pw.println(mGestureRegion);
        pw.print(innerPrefix + "mNavGestureHeight=");
        pw.println(mNavGestureHeight);
        pw.print(innerPrefix + "mIsThreeButtonModeEnabled=");
        pw.println(mIsThreeButtonModeEnabled);
        pw.print(innerPrefix + "mRotation=");
        pw.println(mRotation);
    }

    /**
     * The touch(gesture) events to notify {@link OneHandedController} start or stop one handed
     */
    public interface OneHandedGestureEventCallback {
        /**
         * Handles the start gesture.
         */
        void onStart();

        /**
         * Handles the exit gesture.
         */
        void onStop();
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import java.io.PrintWriter;
/**
 * Manages all the touch handling for One Handed on the Phone, including user tap outside region
 * to exit, reset timer when user is in one-handed mode.
 * Refer {@link OneHandedGestureHandler} to see start and stop one handed gesture
 */
public class OneHandedTouchHandler implements OneHandedTransitionCallback {
    private static final String TAG = "OneHandedTouchHandler";
+0 −31
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -79,8 +78,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
    @Mock
    OneHandedTutorialHandler mMockTutorialHandler;
    @Mock
    OneHandedGestureHandler mMockGestureHandler;
    @Mock
    OneHandedSettingsUtil mMockSettingsUitl;
    @Mock
    OneHandedUiEventLogger mMockUiEventLogger;
@@ -131,7 +128,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
                mMockDisplayAreaOrganizer,
                mMockTouchHandler,
                mMockTutorialHandler,
                mMockGestureHandler,
                mMockSettingsUitl,
                mOneHandedAccessibilityUtil,
                mSpiedTimeoutHandler,
@@ -179,7 +175,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
    @Test
    public void testRegisterTransitionCallbackAfterInit() {
        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTouchHandler);
        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockGestureHandler);
        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTutorialHandler);
    }

@@ -205,7 +200,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
        mSpiedOneHandedController.setOneHandedEnabled(true);

        verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
        verify(mMockGestureHandler, atLeastOnce()).onGestureEnabled(anyBoolean());
    }

    @Test
@@ -213,7 +207,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
        mSpiedOneHandedController.setSwipeToNotificationEnabled(mDefaultSwipeToNotificationEnabled);

        verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
        verify(mMockGestureHandler, atLeastOnce()).onGestureEnabled(anyBoolean());
    }

    @Test
@@ -339,30 +332,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
        verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
    }

    @Test
    public void testDisabled3ButtonGestureWhenKeyguardOn() {
        final boolean isOneHandedEnabled = true;
        final boolean isLockWhenKeyguardOn = true;
        final boolean isEnabledWhenKeyguardOn = false;
        mSpiedOneHandedController.setOneHandedEnabled(isOneHandedEnabled);
        mSpiedOneHandedController.setLockedDisabled(isLockWhenKeyguardOn, isEnabledWhenKeyguardOn);

        verify(mMockGestureHandler).onGestureEnabled(isEnabledWhenKeyguardOn);
    }

    @Test
    public void testEnabled3ButtonGestureWhenKeyguardGoingAway() {
        final boolean isOneHandedEnabled = true;
        final boolean isLockWhenKeyguardOn = false;
        final boolean isEnabledWhenKeyguardOn = false;
        mSpiedOneHandedController.setOneHandedEnabled(isOneHandedEnabled);
        reset(mMockGestureHandler);

        mSpiedOneHandedController.setLockedDisabled(isLockWhenKeyguardOn, isEnabledWhenKeyguardOn);

        verify(mMockGestureHandler).onGestureEnabled(isOneHandedEnabled);
    }

    @Test
    public void testStateActive_shortcutRequestActivate_skipActions() {
        when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE);
Loading