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

Commit 5f2d3992 authored by Bill Lin's avatar Bill Lin
Browse files

The one handed UX of auto-stop one handed by conditions (10/N)

The following condition should stop one handed automatically
1) Task changed(onTaskCreated & onTaskMovedToFront)
2) IME launched
3) Turning off screen
4) Keyguard bouncer showing
5) Camera launch

Test: atest OneHandedDisplayAreaOrganizerTest
Test: atest OneHandedManagerImplTest
Test: atest OneHandedUITest
Test: atest OneHandedSettingsUtilTest
Test: atest SystemUITests
Test: atest DisplayAreaPolicyBuilderTest
Bug: 150747909
Change-Id: Ia7ff4154951ac374e6c2eec285b2831627017fa4
parent 7e63e11d
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -26,6 +26,11 @@ public interface OneHandedManager {
     */
    default void setOneHandedEnabled(boolean enabled) {}

    /**
     * Set task stack changed to exit
     */
    default void setTaskChangeToExit(boolean enabled) {}

    /**
     * Exit one handed mode
     */
+41 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY;

import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;

import android.content.ComponentName;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
@@ -29,6 +30,8 @@ import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.wm.DisplayController;

import java.io.FileDescriptor;
@@ -45,6 +48,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
    private static final String TAG = "OneHandedManager";

    private boolean mIsOneHandedEnabled;
    private boolean mTaskChangeToExit;
    private float mOffSetFraction;
    private DisplayController mDisplayController;
    private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
@@ -54,6 +58,27 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
    private OneHandedTransitionCallback mTransitionCallback;
    private SysUiState mSysUiFlagContainer;

    /**
     * Handler for system task stack changes, exit when user lunch new task or bring task to front
     */
    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
        @Override
        public void onTaskCreated(int taskId, ComponentName componentName) {
            if (!mIsOneHandedEnabled || !mDisplayAreaOrganizer.isInOneHanded()) {
                return;
            }
            stopOneHanded();
        }

        @Override
        public void onTaskMovedToFront(int taskId) {
            if (!mIsOneHandedEnabled || !mDisplayAreaOrganizer.isInOneHanded()) {
                return;
            }
            stopOneHanded();
        }
    };

    /**
     * Constructor of OneHandedManager
     */
@@ -85,6 +110,17 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
        updateOneHandedEnabled();
    }

    /**
     * Set one handed enabled or disabled by OneHanded UI when user update settings
     */
    public void setTaskChangeToExit(boolean enabled) {
        if (mTaskChangeToExit == enabled) {
            return;
        }
        mTaskChangeToExit = enabled;
        updateOneHandedEnabled();
    }

    /**
     * Start one handed mode
     */
@@ -172,6 +208,11 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
            mDisplayAreaOrganizer.registerOrganizer(
                    OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
        }
        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
        if (mTaskChangeToExit) {
            ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
        }
        mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
    }

    @Override
+61 −1
Original line number Diff line number Diff line
@@ -16,15 +16,23 @@

package com.android.systemui.onehanded;

import static android.view.Display.DEFAULT_DISPLAY;

import android.content.Context;
import android.database.ContentObserver;
import android.inputmethodservice.InputMethodService;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.provider.Settings;

import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.SystemUI;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.statusbar.CommandQueue;

import java.io.FileDescriptor;
@@ -45,6 +53,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
    private final OneHandedSettingsUtil mSettingUtil;
    private final OneHandedTimeoutHandler mTimeoutHandler;
    private final ScreenLifecycle mScreenLifecycle;

    private final ContentObserver mEnabledObserver = new ContentObserver(mMainHandler) {
        @Override
@@ -82,7 +91,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
            CommandQueue commandQueue,
            OneHandedManagerImpl oneHandedManager,
            DumpManager dumpManager,
            OneHandedSettingsUtil settingsUtil) {
            OneHandedSettingsUtil settingsUtil,
            ScreenLifecycle screenLifecycle) {
        super(context);

        mCommandQueue = commandQueue;
@@ -92,6 +102,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
        mOneHandedManager = oneHandedManager;
        mSettingUtil =  settingsUtil;
        mTimeoutHandler = OneHandedTimeoutHandler.get();
        mScreenLifecycle = screenLifecycle;
    }

    @Override
@@ -100,6 +111,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
            boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
            if (!supportOneHanded) return; */
        mCommandQueue.addCallback(this);
        setupKeyguardUpdateMonitor();
        setupScreenObserver();
        setupSettingObservers();
        setupTimeoutListener();
        updateSettings();
@@ -109,6 +122,39 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
        mTimeoutHandler.registerTimeoutListener(timeoutTime -> stopOneHanded());
    }

    private void setupKeyguardUpdateMonitor() {
        final KeyguardUpdateMonitorCallback keyguardCallback =
                new KeyguardUpdateMonitorCallback() {
                    @Override
                    public void onKeyguardBouncerChanged(boolean bouncer) {
                        if (bouncer) {
                            stopOneHanded();
                        }
                    }

                    @Override
                    public void onKeyguardVisibilityChanged(boolean showing) {
                        stopOneHanded();
                    }
                };
        Dependency.get(KeyguardUpdateMonitor.class).registerCallback(keyguardCallback);
    }

    @Override
    public void onCameraLaunchGestureDetected(int source) {
        stopOneHanded();
    }

    private void setupScreenObserver() {
        final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
            @Override
            public void onScreenTurningOff() {
                stopOneHanded();
            }
        };
        mScreenLifecycle.addObserver(mScreenObserver);
    }

    private void setupSettingObservers() {
        mSettingUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
                mContext.getContentResolver(), mEnabledObserver);
@@ -123,6 +169,20 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum
                mSettingUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver()));
        mTimeoutHandler.setTimeout(
                mSettingUtil.getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
        mOneHandedManager.setTaskChangeToExit(
                mSettingUtil.getSettingsTapsAppToExit(mContext.getContentResolver()));
    }

    @Override
    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
            boolean showImeSwitcher) {
        if (displayId != DEFAULT_DISPLAY) {
            return;
        }
        if ((vis & InputMethodService.IME_VISIBLE) != 0) {
            // TODO (b/149366439) UIEvent metrics add here, IME trigger to exit
            stopOneHanded();
        }
    }

    /**
+2 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.onehanded;

import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
@@ -51,3 +52,4 @@ public abstract class OneHandedTestCase extends SysuiTestCase {
                Settings.Secure.ONE_HANDED_MODE_TIMEOUT, sOrigTimeout);
    }
}
+24 −1
Original line number Diff line number Diff line
@@ -26,10 +26,13 @@ import android.testing.TestableLooper;

import androidx.test.filters.SmallTest;

import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.statusbar.CommandQueue;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -40,7 +43,9 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper
public class OneHandedUITest extends OneHandedTestCase {
    CommandQueue mCommandQueue;
    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    OneHandedUI mOneHandedUI;
    ScreenLifecycle mScreenLifecycle;
    @Mock
    OneHandedManagerImpl mMockOneHandedManagerImpl;
    @Mock
@@ -54,12 +59,15 @@ public class OneHandedUITest extends OneHandedTestCase {
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mCommandQueue = new CommandQueue(mContext);
        mScreenLifecycle = new ScreenLifecycle();
        mOneHandedUI = new OneHandedUI(mContext,
                mCommandQueue,
                mMockOneHandedManagerImpl,
                mMockDumpManager,
                mMockSettingsUtil);
                mMockSettingsUtil,
                mScreenLifecycle);
        mOneHandedUI.start();
        mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
    }

    @Test
@@ -115,4 +123,19 @@ public class OneHandedUITest extends OneHandedTestCase {
                OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
    }

    @Ignore("Clarifying do not receive callback")
    @Test
    public void testKeyguardBouncerShowing_shouldStopOneHanded() {
        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);

        verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
    }

    @Test
    public void testScreenTurningOff_shouldStopOneHanded() {
        mScreenLifecycle.dispatchScreenTurningOff();

        verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
    }

}