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

Commit 4add47c7 authored by Bill Lin's avatar Bill Lin Committed by Android (Google) Code Review
Browse files

Merge changes I741b1915,Ida2f23da into sc-dev

* changes:
  2/ Migrate isInOneHanded() in OneHandedState
  1/ Add OneHandedState to manage the transition flow
parents 1637653a 0b4a4455
Loading
Loading
Loading
Loading
+51 −48
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@ import static android.os.UserHandle.myUserId;
import static android.view.Display.DEFAULT_DISPLAY;

import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;

import android.annotation.BinderThread;
import android.content.ComponentName;
@@ -62,8 +66,7 @@ import java.io.PrintWriter;
/**
 * Manages and manipulates the one handed states, transitions, and gesture for phones.
 */
public class OneHandedController implements RemoteCallable<OneHandedController>,
        OneHandedTransitionCallback {
public class OneHandedController implements RemoteCallable<OneHandedController> {
    private static final String TAG = "OneHandedController";

    private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -75,7 +78,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,

    private volatile boolean mIsOneHandedEnabled;
    private volatile boolean mIsSwipeToNotificationEnabled;
    private volatile boolean mIsTransitioning;
    private boolean mTaskChangeToExit;
    private boolean mLockedDisabled;
    private int mUserId;
@@ -89,6 +91,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
    private final OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
    private final OneHandedTimeoutHandler mTimeoutHandler;
    private final OneHandedTouchHandler mTouchHandler;
    private final OneHandedState mState;
    private final OneHandedTutorialHandler mTutorialHandler;
    private final OneHandedUiEventLogger mOneHandedUiEventLogger;
    private final TaskStackListenerImpl mTaskStackListener;
@@ -162,6 +165,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
                }
            };

    private final OneHandedTransitionCallback mTransitionCallBack =
            new OneHandedTransitionCallback() {
                @Override
                public void onStartFinished(Rect bounds) {
                    mState.setState(STATE_ACTIVE);
                }

                @Override
                public void onStopFinished(Rect bounds) {
                    mState.setState(STATE_NONE);
                }
            };

    private final TaskStackListenerCallback mTaskStackListenerCallback =
            new TaskStackListenerCallback() {
                @Override
@@ -200,6 +216,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
        OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
        OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
        OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
        OneHandedState transitionState = new OneHandedState();
        OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
                windowManager, mainExecutor);
        OneHandedAnimationController animationController =
@@ -218,7 +235,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,
                gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler, transitionState,
                oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
                mainHandler);
    }
@@ -234,6 +251,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
            OneHandedSettingsUtil settingsUtil,
            OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
            OneHandedTimeoutHandler timeoutHandler,
            OneHandedState state,
            OneHandedUiEventLogger uiEventsLogger,
            IOverlayManager overlayManager,
            TaskStackListenerImpl taskStackListener,
@@ -246,6 +264,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
        mDisplayAreaOrganizer = displayAreaOrganizer;
        mDisplayController = displayController;
        mTouchHandler = touchHandler;
        mState = state;
        mTutorialHandler = tutorialHandler;
        mGestureHandler = gestureHandler;
        mOverlayManager = overlayManager;
@@ -330,17 +349,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,

    @VisibleForTesting
    void startOneHanded() {
        if (isLockedDisabled() || mIsTransitioning) {
        if (isLockedDisabled()) {
            Slog.d(TAG, "Temporary lock disabled");
            return;
        }
        if (mState.isTransitioning() || mState.isInOneHanded()) {
            return;
        }
        final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation();
        if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) {
            Slog.w(TAG, "One handed mode only support portrait mode");
            return;
        }
        if (!mDisplayAreaOrganizer.isInOneHanded()) {
            mIsTransitioning = true;
        mState.setState(STATE_ENTERING);
        final int yOffSet = Math.round(
                mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
        mOneHandedAccessibilityUtil.announcementForScreenReader(
@@ -350,7 +371,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
        mOneHandedUiEventLogger.writeEvent(
                OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
    }
    }

    @VisibleForTesting
    void stopOneHanded() {
@@ -358,15 +378,16 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
    }

    private void stopOneHanded(int uiEvent) {
        if (mDisplayAreaOrganizer.isInOneHanded() && !mIsTransitioning) {
            mIsTransitioning = true;
        if (mState.isTransitioning() || mState.getState() == STATE_NONE) {
            return;
        }
        mState.setState(STATE_EXITING);
        mOneHandedAccessibilityUtil.announcementForScreenReader(
                mOneHandedAccessibilityUtil.getOneHandedStopDescription());
        mDisplayAreaOrganizer.scheduleOffset(0, 0);
        mTimeoutHandler.removeTimer();
        mOneHandedUiEventLogger.writeEvent(uiEvent);
    }
    }

    private void setThreeButtonModeEnabled(boolean enabled) {
        mGestureHandler.onThreeButtonModeEnabled(enabled);
@@ -388,7 +409,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
        mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
        mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
        mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer);
        mDisplayAreaOrganizer.registerTransitionCallback(this);
        mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallBack);
        if (mTaskChangeToExit) {
            mTaskStackListener.addListener(mTaskStackListenerCallback);
        }
@@ -523,8 +544,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
    }

    private void updateOneHandedEnabled() {
        if (mDisplayAreaOrganizer.isInOneHanded()) {
            stopOneHanded();
        if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
            mMainExecutor.execute(() -> stopOneHanded());
        }

        mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
@@ -615,8 +636,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
        pw.println(mLockedDisabled);
        pw.print(innerPrefix + "mUserId=");
        pw.println(mUserId);
        pw.print(innerPrefix + "mIsTransitioning=");
        pw.println(mIsTransitioning);

        if (mBackgroundPanelOrganizer != null) {
            mBackgroundPanelOrganizer.dump(pw);
@@ -638,6 +657,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
            mTimeoutHandler.dump(pw);
        }

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

        if (mTutorialHandler != null) {
            mTutorialHandler.dump(pw);
        }
@@ -662,26 +685,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
        }
    }

    /**
     * TODO(b/185558765) To implement a state machine for One-Handed transition state machine.
     * ONE_HANDDE_STATE_TRANSITION {
     * STATE_DEFAULT,
     * STATE_TRANSITIONING,
     * STATE_ENTER_ONE_HANED,
     * STATE_EXIT_ONE_HANDED
     * }
     * and we need to align the state to launcher3 quick steps through SysuiProxy.
     */
    @Override
    public void onStartFinished(Rect bounds) {
        mIsTransitioning = false;
    }

    @Override
    public void onStopFinished(Rect bounds) {
        mIsTransitioning = false;
    }

    /**
     * The interface for calls from outside the Shell, within the host process.
     */
+0 −15
Original line number Diff line number Diff line
@@ -65,7 +65,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
    private final Rect mDefaultDisplayBounds = new Rect();
    private final OneHandedSettingsUtil mOneHandedSettingsUtil;

    private boolean mIsInOneHanded;
    private int mEnterExitAnimationDurationMs;

    private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
@@ -268,9 +267,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
    @VisibleForTesting
    void finishOffset(int offset,
            @OneHandedAnimationController.TransitionDirection int direction) {
        // Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence,
        // the flag *MUST* be updated before dispatch mTransitionCallbacks
        mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER);
        mLastVisualDisplayBounds.offsetTo(0,
                direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0);
        for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
@@ -284,15 +280,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
        }
    }

    /**
     * The latest state of one handed mode
     *
     * @return true Currently is in one handed mode, otherwise is not in one handed mode
     */
    public boolean isInOneHanded() {
        return mIsInOneHanded;
    }

    /**
     * The latest visual bounds of displayArea translated
     *
@@ -337,8 +324,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
    void dump(@NonNull PrintWriter pw) {
        final String innerPrefix = "  ";
        pw.println(TAG + "states: ");
        pw.print(innerPrefix + "mIsInOneHanded=");
        pw.println(mIsInOneHanded);
        pw.print(innerPrefix + "mDisplayLayout.rotation()=");
        pw.println(mDisplayLayout.rotation());
        pw.print(innerPrefix + "mDisplayAreaTokenMap=");
+96 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 android.annotation.IntDef;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 Represents current OHM state by following steps, a generic CUJ is
 STATE_NONE -> STATE_ENTERING -> STATE_ACTIVE -> STATE_EXITING -> STATE_NONE
 */
public class OneHandedState {
    /** DEFAULT STATE after OHM feature initialized. */
    public static final int STATE_NONE = 0x00000000;
    /** The state flag set when user trigger OHM. */
    public static final int STATE_ENTERING = 0x00000001;
    /** The state flag set when transitioning */
    public static final int STATE_ACTIVE = 0x00000002;
    /** The state flag set when user stop OHM feature. */
    public static final int STATE_EXITING = 0x00000004;

    @IntDef(prefix = { "STATE_" }, value =  {
            STATE_NONE,
            STATE_ENTERING,
            STATE_ACTIVE,
            STATE_EXITING
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface State {}

    public OneHandedState() {
        sCurrentState = STATE_NONE;
    }

    @State
    private static int sCurrentState = STATE_NONE;

    private static final String TAG = OneHandedState.class.getSimpleName();

    /**
     * Gets current transition state of One handed mode.
     *
     * @return The bitwise flags representing current states.
     */
    public @State int getState() {
        return sCurrentState;
    }

    /**
     * Is the One handed mode is in transitioning state.
     * @return true if One handed mode is in transitioning states.
     */
    public boolean isTransitioning() {
        return sCurrentState == STATE_ENTERING || sCurrentState == STATE_EXITING;
    }

    /**
     * Is the One handed mode active state.
     * @return true if One handed mode is active state.
     */
    public boolean isInOneHanded() {
        return sCurrentState == STATE_ACTIVE;
    }

    /**
     * Sets new state for One handed mode feature.
     * @param newState The bitwise value to represent current transition states.
     */
    public void setState(@State int newState) {
        sCurrentState = newState;
    }

    /** Dumps internal state. */
    public void dump(PrintWriter pw) {
        final String innerPrefix = "  ";
        pw.println(TAG + "states: ");
        pw.println(innerPrefix + "sCurrentState=" + sCurrentState);
    }
}
+17 −17
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.wm.shell.onehanded;

import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
@@ -61,6 +64,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
    OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
    OneHandedController mSpiedOneHandedController;
    OneHandedTimeoutHandler mSpiedTimeoutHandler;
    OneHandedState mSpiedTransitionState;

    @Mock
    DisplayController mMockDisplayController;
@@ -99,9 +103,9 @@ public class OneHandedControllerTest extends OneHandedTestCase {
        mDisplay = mContext.getDisplay();
        mDisplayLayout = new DisplayLayout(mContext, mDisplay);
        mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
        mSpiedTransitionState = spy(new OneHandedState());

        when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
        when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
        when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
@@ -129,6 +133,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
                mMockSettingsUitl,
                mOneHandedAccessibilityUtil,
                mSpiedTimeoutHandler,
                mSpiedTransitionState,
                mMockUiEventLogger,
                mMockOverlayManager,
                mMockTaskStackListener,
@@ -139,18 +144,13 @@ public class OneHandedControllerTest extends OneHandedTestCase {

    @Test
    public void testDefaultShouldNotInOneHanded() {
        final OneHandedAnimationController animationController = new OneHandedAnimationController(
                mContext);
        OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
                mContext, mDisplayLayout, mMockSettingsUitl, animationController,
                mMockTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor);

        assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
        // Assert default transition state is STATE_NONE
        assertThat(mSpiedTransitionState.getState()).isEqualTo(STATE_NONE);
    }

    @Test
    public void testStartOneHandedShouldTriggerScheduleOffset() {
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        mSpiedTransitionState.setState(STATE_NONE);
        mSpiedOneHandedController.setOneHandedEnabled(true);
        mSpiedOneHandedController.startOneHanded();

@@ -160,7 +160,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
    @Test
    public void testStartOneHandedShouldNotTriggerScheduleOffset() {
        mSpiedOneHandedController.setOneHandedEnabled(true);
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true);
        mSpiedTransitionState.setState(STATE_ENTERING);
        mSpiedOneHandedController.startOneHanded();

        verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
@@ -168,7 +168,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {

    @Test
    public void testStopOneHanded() {
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        mSpiedTransitionState.setState(STATE_NONE);
        mSpiedOneHandedController.stopOneHanded();

        verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
@@ -192,7 +192,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {

    @Test
    public void testStopOneHandedShouldRemoveTimer() {
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true);
        mSpiedTransitionState.setState(STATE_ENTERING);
        mSpiedOneHandedController.stopOneHanded();

        verify(mSpiedTimeoutHandler, atLeastOnce()).removeTimer();
@@ -280,7 +280,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {

    @Test
    public void testKeyguardShowingLockOneHandedDisabled() {
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        mSpiedTransitionState.setState(STATE_NONE);
        mSpiedOneHandedController.setOneHandedEnabled(true);
        mSpiedOneHandedController.setLockedDisabled(true /* locked */, false /* enabled */);
        mSpiedOneHandedController.startOneHanded();
@@ -290,7 +290,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {

    @Test
    public void testResetKeyguardShowingLockOneHandedDisabled() {
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        mSpiedTransitionState.setState(STATE_NONE);
        mSpiedOneHandedController.setOneHandedEnabled(true);
        mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
        mSpiedOneHandedController.startOneHanded();
@@ -302,7 +302,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
    public void testRotation90CanNotStartOneHanded() {
        final DisplayLayout landscapeDisplayLayout = new DisplayLayout(mDisplayLayout);
        landscapeDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        mSpiedTransitionState.setState(STATE_NONE);
        when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(landscapeDisplayLayout);
        mSpiedOneHandedController.setOneHandedEnabled(true);
        mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
@@ -315,7 +315,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
    public void testRotation180CanStartOneHanded() {
        final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout);
        testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        mSpiedTransitionState.setState(STATE_NONE);
        when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout);
        mSpiedOneHandedController.setOneHandedEnabled(true);
        mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
@@ -328,7 +328,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
    public void testRotation270CanNotStartOneHanded() {
        final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout);
        testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        mSpiedTransitionState.setState(STATE_NONE);
        when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout);
        mSpiedOneHandedController.setOneHandedEnabled(true);
        mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
+221 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading