Fix Talk back announces "Exit one handed mode" on every orientation
change though one-handed mode is off Add more condition check when Talk back announce one-handed mode related. Bug: 182475904 Test: manual Test: atest WMShellUnitTests Change-Id: I0644146eaa6d7d6701af94fe6caceee5e6c3be8b
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.content.Context;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* The util for handling A11y events.
|
||||
*/
|
||||
public final class OneHandedAccessibilityUtil {
|
||||
private static final String TAG = "OneHandedAccessibilityUtil";
|
||||
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private final String mStartOneHandedDescription;
|
||||
private final String mStopOneHandedDescription;
|
||||
private final String mPackageName;
|
||||
|
||||
private String mDescription;
|
||||
|
||||
public OneHandedAccessibilityUtil(Context context) {
|
||||
mAccessibilityManager = AccessibilityManager.getInstance(context);
|
||||
mPackageName = context.getPackageName();
|
||||
mStartOneHandedDescription = context.getResources().getString(
|
||||
R.string.accessibility_action_start_one_handed);
|
||||
mStopOneHandedDescription = context.getResources().getString(
|
||||
R.string.accessibility_action_stop_one_handed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets One-Handed start description.
|
||||
* @return text of start description.
|
||||
*/
|
||||
public String getOneHandedStartDescription() {
|
||||
return mStartOneHandedDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets One-Handed stop description.
|
||||
* @return text of stop description.
|
||||
*/
|
||||
public String getOneHandedStopDescription() {
|
||||
return mStopOneHandedDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Announcement of A11y Events
|
||||
* @param description for accessibility announcement text
|
||||
*/
|
||||
public void announcementForScreenReader(String description) {
|
||||
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
|
||||
return;
|
||||
}
|
||||
mDescription = description;
|
||||
final AccessibilityEvent event = AccessibilityEvent.obtain();
|
||||
event.setPackageName(mPackageName);
|
||||
event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
|
||||
event.getText().add(mDescription);
|
||||
mAccessibilityManager.sendAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
public void dump(@NonNull PrintWriter pw) {
|
||||
final String innerPrefix = " ";
|
||||
pw.println(TAG + "States: ");
|
||||
pw.print(innerPrefix + "mPackageName=");
|
||||
pw.println(mPackageName);
|
||||
pw.print(innerPrefix + "mDescription=");
|
||||
pw.println(mDescription);
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private final DisplayController mDisplayController;
|
||||
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
|
||||
private final OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
|
||||
private final OneHandedTimeoutHandler mTimeoutHandler;
|
||||
private final OneHandedTouchHandler mTouchHandler;
|
||||
private final OneHandedTutorialHandler mTutorialHandler;
|
||||
@@ -193,6 +194,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
return null;
|
||||
}
|
||||
|
||||
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
|
||||
OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
|
||||
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
|
||||
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
|
||||
windowManager, mainExecutor);
|
||||
@@ -205,16 +208,16 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
|
||||
new OneHandedBackgroundPanelOrganizer(context, displayLayout, mainExecutor);
|
||||
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
|
||||
context, displayLayout, animationController, tutorialHandler,
|
||||
context, displayLayout, settingsUtil, animationController, tutorialHandler,
|
||||
oneHandedBackgroundPanelOrganizer, mainExecutor);
|
||||
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
|
||||
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
|
||||
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.OVERLAY_SERVICE));
|
||||
return new OneHandedController(context, displayController,
|
||||
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
|
||||
gestureHandler, settingsUtil, timeoutHandler, oneHandedUiEventsLogger,
|
||||
overlayManager, taskStackListener, mainExecutor, mainHandler);
|
||||
gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler,
|
||||
oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
|
||||
mainHandler);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -226,6 +229,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
OneHandedTutorialHandler tutorialHandler,
|
||||
OneHandedGestureHandler gestureHandler,
|
||||
OneHandedSettingsUtil settingsUtil,
|
||||
OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
|
||||
OneHandedTimeoutHandler timeoutHandler,
|
||||
OneHandedUiEventLogger uiEventsLogger,
|
||||
IOverlayManager overlayManager,
|
||||
@@ -234,6 +238,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
Handler mainHandler) {
|
||||
mContext = context;
|
||||
mOneHandedSettingsUtil = settingsUtil;
|
||||
mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
|
||||
mBackgroundPanelOrganizer = backgroundPanelOrganizer;
|
||||
mDisplayAreaOrganizer = displayAreaOrganizer;
|
||||
mDisplayController = displayController;
|
||||
@@ -334,6 +339,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
if (!mDisplayAreaOrganizer.isInOneHanded()) {
|
||||
final int yOffSet = Math.round(
|
||||
mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
|
||||
mOneHandedAccessibilityUtil.announcementForScreenReader(
|
||||
mOneHandedAccessibilityUtil.getOneHandedStartDescription());
|
||||
mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
|
||||
mTimeoutHandler.resetTimer();
|
||||
|
||||
@@ -345,6 +352,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
@VisibleForTesting
|
||||
void stopOneHanded() {
|
||||
if (mDisplayAreaOrganizer.isInOneHanded()) {
|
||||
mOneHandedAccessibilityUtil.announcementForScreenReader(
|
||||
mOneHandedAccessibilityUtil.getOneHandedStopDescription());
|
||||
mDisplayAreaOrganizer.scheduleOffset(0, 0);
|
||||
mTimeoutHandler.removeTimer();
|
||||
}
|
||||
@@ -352,6 +361,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
|
||||
private void stopOneHanded(int uiEvent) {
|
||||
if (mDisplayAreaOrganizer.isInOneHanded()) {
|
||||
mOneHandedAccessibilityUtil.announcementForScreenReader(
|
||||
mOneHandedAccessibilityUtil.getOneHandedStopDescription());
|
||||
mDisplayAreaOrganizer.scheduleOffset(0, 0);
|
||||
mTimeoutHandler.removeTimer();
|
||||
mOneHandedUiEventLogger.writeEvent(uiEvent);
|
||||
@@ -629,6 +640,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
||||
mTutorialHandler.dump(pw);
|
||||
}
|
||||
|
||||
if (mOneHandedAccessibilityUtil != null) {
|
||||
mOneHandedAccessibilityUtil.dump(pw);
|
||||
}
|
||||
|
||||
mOneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver(), mUserId);
|
||||
|
||||
if (mOverlayManager != null) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.wm.shell.onehanded;
|
||||
|
||||
import static android.os.UserHandle.myUserId;
|
||||
|
||||
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
|
||||
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
|
||||
|
||||
@@ -61,6 +63,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
|
||||
|
||||
private final Rect mLastVisualDisplayBounds = new Rect();
|
||||
private final Rect mDefaultDisplayBounds = new Rect();
|
||||
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
|
||||
|
||||
private boolean mIsInOneHanded;
|
||||
private int mEnterExitAnimationDurationMs;
|
||||
@@ -109,12 +112,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
|
||||
*/
|
||||
public OneHandedDisplayAreaOrganizer(Context context,
|
||||
DisplayLayout displayLayout,
|
||||
OneHandedSettingsUtil oneHandedSettingsUtil,
|
||||
OneHandedAnimationController animationController,
|
||||
OneHandedTutorialHandler tutorialHandler,
|
||||
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
|
||||
ShellExecutor mainExecutor) {
|
||||
super(mainExecutor);
|
||||
mDisplayLayout.set(displayLayout);
|
||||
mOneHandedSettingsUtil = oneHandedSettingsUtil;
|
||||
updateDisplayBounds();
|
||||
mAnimationController = animationController;
|
||||
final int animationDurationConfig = context.getResources().getInteger(
|
||||
@@ -168,6 +173,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
|
||||
if (mDisplayLayout.rotation() == toRotation) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(context.getContentResolver(),
|
||||
myUserId())) {
|
||||
return;
|
||||
}
|
||||
mDisplayLayout.rotateTo(context.getResources(), toRotation);
|
||||
resetWindowsOffset(wct);
|
||||
updateDisplayBounds();
|
||||
|
||||
@@ -28,8 +28,6 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -51,7 +49,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
|
||||
"persist.debug.one_handed_offset_percentage";
|
||||
private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
|
||||
private final WindowManager mWindowManager;
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private final String mPackageName;
|
||||
private final Rect mDisplaySize;
|
||||
|
||||
@@ -59,8 +56,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
|
||||
private View mTutorialView;
|
||||
private ContentResolver mContentResolver;
|
||||
private boolean mCanShowTutorial;
|
||||
private String mStartOneHandedDescription;
|
||||
private String mStopOneHandedDescription;
|
||||
private boolean mIsOneHandedMode;
|
||||
|
||||
private enum ONE_HANDED_TRIGGER_STATE {
|
||||
@@ -106,11 +101,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
|
||||
mDisplaySize = windowManager.getCurrentWindowMetrics().getBounds();
|
||||
mPackageName = context.getPackageName();
|
||||
mContentResolver = context.getContentResolver();
|
||||
mAccessibilityManager = AccessibilityManager.getInstance(context);
|
||||
mStartOneHandedDescription = context.getResources().getString(
|
||||
R.string.accessibility_action_start_one_handed);
|
||||
mStopOneHandedDescription = context.getResources().getString(
|
||||
R.string.accessibility_action_stop_one_handed);
|
||||
mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
|
||||
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
|
||||
? false : true;
|
||||
@@ -131,14 +121,12 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
|
||||
public void onStartFinished(Rect bounds) {
|
||||
updateFinished(View.VISIBLE, 0f);
|
||||
updateTutorialCount();
|
||||
announcementForScreenReader(true);
|
||||
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopFinished(Rect bounds) {
|
||||
updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
|
||||
announcementForScreenReader(false);
|
||||
removeTutorialFromWindowManager();
|
||||
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
|
||||
}
|
||||
@@ -170,17 +158,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
|
||||
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount);
|
||||
}
|
||||
|
||||
private void announcementForScreenReader(boolean isStartOneHanded) {
|
||||
if (mAccessibilityManager.isTouchExplorationEnabled()) {
|
||||
final AccessibilityEvent event = AccessibilityEvent.obtain();
|
||||
event.setPackageName(mPackageName);
|
||||
event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
|
||||
event.getText().add(isStartOneHanded
|
||||
? mStartOneHandedDescription : mStopOneHandedDescription);
|
||||
mAccessibilityManager.sendAccessibilityEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the tutorial target view to the WindowManager and update its layout, so it's ready
|
||||
* to be animated in.
|
||||
|
||||
@@ -58,6 +58,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
|
||||
|
||||
Display mDisplay;
|
||||
DisplayLayout mDisplayLayout;
|
||||
OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
|
||||
OneHandedController mSpiedOneHandedController;
|
||||
OneHandedTimeoutHandler mSpiedTimeoutHandler;
|
||||
|
||||
@@ -116,6 +117,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
|
||||
new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height()));
|
||||
when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mDisplayLayout);
|
||||
|
||||
mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
|
||||
mSpiedOneHandedController = spy(new OneHandedController(
|
||||
mContext,
|
||||
mMockDisplayController,
|
||||
@@ -125,6 +127,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
|
||||
mMockTutorialHandler,
|
||||
mMockGestureHandler,
|
||||
mMockSettingsUitl,
|
||||
mOneHandedAccessibilityUtil,
|
||||
mSpiedTimeoutHandler,
|
||||
mMockUiEventLogger,
|
||||
mMockOverlayManager,
|
||||
@@ -139,8 +142,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
|
||||
final OneHandedAnimationController animationController = new OneHandedAnimationController(
|
||||
mContext);
|
||||
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
|
||||
mContext, mDisplayLayout, animationController, mMockTutorialHandler,
|
||||
mMockBackgroundOrganizer, mMockShellMainExecutor);
|
||||
mContext, mDisplayLayout, mMockSettingsUitl, animationController,
|
||||
mMockTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor);
|
||||
|
||||
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
|
||||
}
|
||||
|
||||
@@ -97,9 +97,13 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
|
||||
OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
|
||||
@Mock
|
||||
ShellExecutor mMockShellMainExecutor;
|
||||
@Mock
|
||||
OneHandedSettingsUtil mMockSettingsUitl;
|
||||
|
||||
List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>();
|
||||
|
||||
final boolean mDefaultEnabled = true;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -126,9 +130,12 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
|
||||
when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator);
|
||||
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
|
||||
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
|
||||
when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
|
||||
mDefaultEnabled);
|
||||
|
||||
mSpiedDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
|
||||
mDisplayLayout,
|
||||
mMockSettingsUitl,
|
||||
mMockAnimationController,
|
||||
mTutorialHandler,
|
||||
mMockBackgroundOrganizer,
|
||||
|
||||
@@ -66,6 +66,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
|
||||
OneHandedUiEventLogger mMockUiEventLogger;
|
||||
@Mock
|
||||
OneHandedSettingsUtil mMockSettingsUtil;
|
||||
@Mock
|
||||
OneHandedAccessibilityUtil mMockAccessibilityUtil;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -82,6 +84,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
|
||||
mMockTutorialHandler,
|
||||
mMockGestureHandler,
|
||||
mMockSettingsUtil,
|
||||
mMockAccessibilityUtil,
|
||||
mTimeoutHandler,
|
||||
mMockUiEventLogger,
|
||||
mMockOverlayManager,
|
||||
|
||||
Reference in New Issue
Block a user