Loading libs/WindowManager/Shell/res/values/config.xml +1 −1 Original line number Diff line number Diff line Loading @@ -40,7 +40,7 @@ <integer name="long_press_dock_anim_duration">250</integer> <!-- Animation duration for translating of one handed when trigger / dismiss. --> <integer name="config_one_handed_translate_animation_duration">800</integer> <integer name="config_one_handed_translate_animation_duration">600</integer> <!-- One handed mode default offset % of display size --> <fraction name="config_one_handed_offset">40%</fraction> Loading libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +5 −0 Original line number Diff line number Diff line Loading @@ -67,6 +67,11 @@ public interface OneHanded { */ void setLockedDisabled(boolean locked, boolean enabled); /** * Registers callback to notify WMShell when user tap shortcut to expand notification. */ void registerEventCallback(OneHandedEventCallback callback); /** * Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer} * transition start or finish Loading libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +68 −10 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.util.Slog; import android.view.Surface; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; Loading Loading @@ -72,6 +73,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY = "com.android.internal.systemui.onehanded.gestural"; private static final int OVERLAY_ENABLED_DELAY_MS = 250; private static final int DISPLAY_AREA_READY_RETRY_MS = 10; static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; Loading Loading @@ -99,6 +101,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private final Handler mMainHandler; private final OneHandedImpl mImpl = new OneHandedImpl(); private OneHandedEventCallback mEventCallback; private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer; Loading Loading @@ -288,7 +291,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mTimeoutObserver = getObserver(this::onTimeoutSettingChanged); mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged); mSwipeToNotificationEnabledObserver = getObserver(this::onSwipeToNotificationEnabledSettingChanged); getObserver(this::onSwipeToNotificationEnabledChanged); mDisplayController.addDisplayChangingController(mRotationController); setupCallback(); Loading Loading @@ -358,14 +361,23 @@ public class OneHandedController implements RemoteCallable<OneHandedController> Slog.d(TAG, "Temporary lock disabled"); return; } if (!mDisplayAreaOrganizer.isReady()) { // Must wait until DisplayAreaOrganizer is ready for transitioning. mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS); 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; } mState.setState(STATE_ENTERING); final int yOffSet = Math.round( mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); Loading Loading @@ -394,6 +406,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mOneHandedUiEventLogger.writeEvent(uiEvent); } void registerEventCallback(OneHandedEventCallback callback) { mEventCallback = callback; } @VisibleForTesting void registerTransitionCallback(OneHandedTransitionCallback callback) { mDisplayAreaOrganizer.registerTransitionCallback(callback); Loading Loading @@ -463,9 +479,30 @@ public class OneHandedController implements RemoteCallable<OneHandedController> }; } @VisibleForTesting void notifyExpandNotification() { mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification()); } @VisibleForTesting void notifyUserConfigChanged(boolean success) { if (!success) { return; } // TODO Check UX if popup Toast to notify user when auto-enabled one-handed is good option. Toast.makeText(mContext, R.string.one_handed_tutorial_title, Toast.LENGTH_LONG).show(); } @VisibleForTesting void onActivatedActionChanged() { if (mState.isTransitioning() || !isOneHandedEnabled()) { if (!isOneHandedEnabled()) { final boolean success = mOneHandedSettingsUtil.setOneHandedModeEnabled( mContext.getContentResolver(), 1 /* Enabled for shortcut */, mUserId); notifyUserConfigChanged(success); } if (isSwipeToNotificationEnabled()) { notifyExpandNotification(); return; } Loading Loading @@ -494,11 +531,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController> setOneHandedEnabled(enabled); // Also checks swipe to notification settings since they all need gesture overlay. // Enabled overlay package may affect the current animation(e.g:Settings switch), // so we delay 250ms to enabled overlay after switch animation finish mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay( setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver(), mUserId)), OVERLAY_ENABLED_DELAY_MS); mContext.getContentResolver(), mUserId), true /* DelayExecute */); } @VisibleForTesting Loading Loading @@ -542,7 +577,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @VisibleForTesting void onSwipeToNotificationEnabledSettingChanged() { void onSwipeToNotificationEnabledChanged() { final boolean enabled = mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver(), mUserId); Loading @@ -551,7 +586,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> // Also checks one handed mode settings since they all need gesture overlay. setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( mContext.getContentResolver(), mUserId)); mContext.getContentResolver(), mUserId), true /* DelayExecute */); } private void setupTimeoutListener() { Loading @@ -569,11 +604,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController> return mIsOneHandedEnabled; } @VisibleForTesting boolean isSwipeToNotificationEnabled() { return mIsSwipeToNotificationEnabled; } private void updateOneHandedEnabled() { if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) { mMainExecutor.execute(() -> stopOneHanded()); } // Reset and align shortcut one_handed_mode_activated status with current mState notifyShortcutState(mState.getState()); mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); if (!mIsOneHandedEnabled) { Loading Loading @@ -608,12 +651,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController> if (info != null && !info.isEnabled()) { // Enable the default gestural one handed overlay. setEnabledGesturalOverlay(true); setEnabledGesturalOverlay(true /* enabled */, false /* delayExecute */); } } @VisibleForTesting private void setEnabledGesturalOverlay(boolean enabled) { private void setEnabledGesturalOverlay(boolean enabled, boolean delayExecute) { if (mState.isTransitioning() || delayExecute) { // Enabled overlay package may affect the current animation(e.g:Settings switch), // so we delay 250ms to enabled overlay after switch animation finish, only delay once. mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(enabled, false), OVERLAY_ENABLED_DELAY_MS); return; } try { mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT); } catch (RemoteException e) { Loading @@ -628,6 +678,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> if (enabled == isFeatureEnabled) { return; } mLockedDisabled = locked && !enabled; } Loading Loading @@ -760,6 +811,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController> }); } @Override public void registerEventCallback(OneHandedEventCallback callback) { mMainExecutor.execute(() -> { OneHandedController.this.registerEventCallback(callback); }); } @Override public void registerTransitionCallback(OneHandedTransitionCallback callback) { mMainExecutor.execute(() -> { Loading libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +10 −1 Original line number Diff line number Diff line Loading @@ -61,11 +61,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private DisplayLayout mDisplayLayout = new DisplayLayout(); private float mLastVisualOffset = 0; private final Rect mLastVisualDisplayBounds = new Rect(); private final Rect mDefaultDisplayBounds = new Rect(); private final OneHandedSettingsUtil mOneHandedSettingsUtil; private boolean mIsReady; private float mLastVisualOffset = 0; private int mEnterExitAnimationDurationMs; private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); Loading Loading @@ -157,6 +158,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { final DisplayAreaAppearedInfo info = displayAreaInfos.get(i); onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash()); } mIsReady = true; updateDisplayBounds(); return displayAreaInfos; } Loading @@ -164,9 +166,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void unregisterOrganizer() { super.unregisterOrganizer(); mIsReady = false; resetWindowsOffset(); } boolean isReady() { return mIsReady; } /** * Handler for display rotation changes by {@link DisplayLayout} * Loading Loading @@ -312,6 +319,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { pw.println(mDisplayAreaTokenMap); pw.print(innerPrefix + "mDefaultDisplayBounds="); pw.println(mDefaultDisplayBounds); pw.print(innerPrefix + "mIsReady="); pw.println(mIsReady); pw.print(innerPrefix + "mLastVisualDisplayBounds="); pw.println(mLastVisualDisplayBounds); pw.print(innerPrefix + "mLastVisualOffset="); Loading libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java 0 → 100644 +28 −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; /** * Additional callback interface for OneHanded events. */ public interface OneHandedEventCallback { /** * Called to notify expand notification shade. */ default void notifyExpandNotification() { } } Loading
libs/WindowManager/Shell/res/values/config.xml +1 −1 Original line number Diff line number Diff line Loading @@ -40,7 +40,7 @@ <integer name="long_press_dock_anim_duration">250</integer> <!-- Animation duration for translating of one handed when trigger / dismiss. --> <integer name="config_one_handed_translate_animation_duration">800</integer> <integer name="config_one_handed_translate_animation_duration">600</integer> <!-- One handed mode default offset % of display size --> <fraction name="config_one_handed_offset">40%</fraction> Loading
libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +5 −0 Original line number Diff line number Diff line Loading @@ -67,6 +67,11 @@ public interface OneHanded { */ void setLockedDisabled(boolean locked, boolean enabled); /** * Registers callback to notify WMShell when user tap shortcut to expand notification. */ void registerEventCallback(OneHandedEventCallback callback); /** * Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer} * transition start or finish Loading
libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +68 −10 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.util.Slog; import android.view.Surface; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; Loading Loading @@ -72,6 +73,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY = "com.android.internal.systemui.onehanded.gestural"; private static final int OVERLAY_ENABLED_DELAY_MS = 250; private static final int DISPLAY_AREA_READY_RETRY_MS = 10; static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; Loading Loading @@ -99,6 +101,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private final Handler mMainHandler; private final OneHandedImpl mImpl = new OneHandedImpl(); private OneHandedEventCallback mEventCallback; private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer; Loading Loading @@ -288,7 +291,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mTimeoutObserver = getObserver(this::onTimeoutSettingChanged); mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged); mSwipeToNotificationEnabledObserver = getObserver(this::onSwipeToNotificationEnabledSettingChanged); getObserver(this::onSwipeToNotificationEnabledChanged); mDisplayController.addDisplayChangingController(mRotationController); setupCallback(); Loading Loading @@ -358,14 +361,23 @@ public class OneHandedController implements RemoteCallable<OneHandedController> Slog.d(TAG, "Temporary lock disabled"); return; } if (!mDisplayAreaOrganizer.isReady()) { // Must wait until DisplayAreaOrganizer is ready for transitioning. mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS); 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; } mState.setState(STATE_ENTERING); final int yOffSet = Math.round( mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); Loading Loading @@ -394,6 +406,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mOneHandedUiEventLogger.writeEvent(uiEvent); } void registerEventCallback(OneHandedEventCallback callback) { mEventCallback = callback; } @VisibleForTesting void registerTransitionCallback(OneHandedTransitionCallback callback) { mDisplayAreaOrganizer.registerTransitionCallback(callback); Loading Loading @@ -463,9 +479,30 @@ public class OneHandedController implements RemoteCallable<OneHandedController> }; } @VisibleForTesting void notifyExpandNotification() { mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification()); } @VisibleForTesting void notifyUserConfigChanged(boolean success) { if (!success) { return; } // TODO Check UX if popup Toast to notify user when auto-enabled one-handed is good option. Toast.makeText(mContext, R.string.one_handed_tutorial_title, Toast.LENGTH_LONG).show(); } @VisibleForTesting void onActivatedActionChanged() { if (mState.isTransitioning() || !isOneHandedEnabled()) { if (!isOneHandedEnabled()) { final boolean success = mOneHandedSettingsUtil.setOneHandedModeEnabled( mContext.getContentResolver(), 1 /* Enabled for shortcut */, mUserId); notifyUserConfigChanged(success); } if (isSwipeToNotificationEnabled()) { notifyExpandNotification(); return; } Loading Loading @@ -494,11 +531,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController> setOneHandedEnabled(enabled); // Also checks swipe to notification settings since they all need gesture overlay. // Enabled overlay package may affect the current animation(e.g:Settings switch), // so we delay 250ms to enabled overlay after switch animation finish mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay( setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver(), mUserId)), OVERLAY_ENABLED_DELAY_MS); mContext.getContentResolver(), mUserId), true /* DelayExecute */); } @VisibleForTesting Loading Loading @@ -542,7 +577,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @VisibleForTesting void onSwipeToNotificationEnabledSettingChanged() { void onSwipeToNotificationEnabledChanged() { final boolean enabled = mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver(), mUserId); Loading @@ -551,7 +586,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> // Also checks one handed mode settings since they all need gesture overlay. setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( mContext.getContentResolver(), mUserId)); mContext.getContentResolver(), mUserId), true /* DelayExecute */); } private void setupTimeoutListener() { Loading @@ -569,11 +604,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController> return mIsOneHandedEnabled; } @VisibleForTesting boolean isSwipeToNotificationEnabled() { return mIsSwipeToNotificationEnabled; } private void updateOneHandedEnabled() { if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) { mMainExecutor.execute(() -> stopOneHanded()); } // Reset and align shortcut one_handed_mode_activated status with current mState notifyShortcutState(mState.getState()); mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); if (!mIsOneHandedEnabled) { Loading Loading @@ -608,12 +651,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController> if (info != null && !info.isEnabled()) { // Enable the default gestural one handed overlay. setEnabledGesturalOverlay(true); setEnabledGesturalOverlay(true /* enabled */, false /* delayExecute */); } } @VisibleForTesting private void setEnabledGesturalOverlay(boolean enabled) { private void setEnabledGesturalOverlay(boolean enabled, boolean delayExecute) { if (mState.isTransitioning() || delayExecute) { // Enabled overlay package may affect the current animation(e.g:Settings switch), // so we delay 250ms to enabled overlay after switch animation finish, only delay once. mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(enabled, false), OVERLAY_ENABLED_DELAY_MS); return; } try { mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT); } catch (RemoteException e) { Loading @@ -628,6 +678,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> if (enabled == isFeatureEnabled) { return; } mLockedDisabled = locked && !enabled; } Loading Loading @@ -760,6 +811,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController> }); } @Override public void registerEventCallback(OneHandedEventCallback callback) { mMainExecutor.execute(() -> { OneHandedController.this.registerEventCallback(callback); }); } @Override public void registerTransitionCallback(OneHandedTransitionCallback callback) { mMainExecutor.execute(() -> { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +10 −1 Original line number Diff line number Diff line Loading @@ -61,11 +61,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private DisplayLayout mDisplayLayout = new DisplayLayout(); private float mLastVisualOffset = 0; private final Rect mLastVisualDisplayBounds = new Rect(); private final Rect mDefaultDisplayBounds = new Rect(); private final OneHandedSettingsUtil mOneHandedSettingsUtil; private boolean mIsReady; private float mLastVisualOffset = 0; private int mEnterExitAnimationDurationMs; private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); Loading Loading @@ -157,6 +158,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { final DisplayAreaAppearedInfo info = displayAreaInfos.get(i); onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash()); } mIsReady = true; updateDisplayBounds(); return displayAreaInfos; } Loading @@ -164,9 +166,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void unregisterOrganizer() { super.unregisterOrganizer(); mIsReady = false; resetWindowsOffset(); } boolean isReady() { return mIsReady; } /** * Handler for display rotation changes by {@link DisplayLayout} * Loading Loading @@ -312,6 +319,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { pw.println(mDisplayAreaTokenMap); pw.print(innerPrefix + "mDefaultDisplayBounds="); pw.println(mDefaultDisplayBounds); pw.print(innerPrefix + "mIsReady="); pw.println(mIsReady); pw.print(innerPrefix + "mLastVisualDisplayBounds="); pw.println(mLastVisualDisplayBounds); pw.print(innerPrefix + "mLastVisualOffset="); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java 0 → 100644 +28 −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; /** * Additional callback interface for OneHanded events. */ public interface OneHandedEventCallback { /** * Called to notify expand notification shade. */ default void notifyExpandNotification() { } }