Merge changes from topic "ohm-shortcut" into sc-dev

* changes:
  3/ Listen ONE_HANDED_MODE_ACTIVATED for A11y shortcut action
  2/ Implement Shortcut for One Handed mode feature
This commit is contained in:
Bill Lin
2021-05-05 23:43:12 +00:00
committed by Android (Google) Code Review
9 changed files with 208 additions and 3 deletions

View File

@@ -80,6 +80,8 @@ public class AccessibilityShortcutController {
"com.android.server.accessibility.MagnificationController";
public static final ComponentName MAGNIFICATION_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "Magnification");
public static final ComponentName ONE_HANDED_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "OneHandedMode");
public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
@@ -117,7 +119,7 @@ public class AccessibilityShortcutController {
public static Map<ComponentName, ToggleableFrameworkFeatureInfo>
getFrameworkShortcutFeaturesMap() {
if (sFrameworkShortcutFeaturesMap == null) {
Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(2);
Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(4);
featuresMap.put(COLOR_INVERSION_COMPONENT_NAME,
new ToggleableFrameworkFeatureInfo(
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
@@ -128,6 +130,11 @@ public class AccessibilityShortcutController {
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
"1" /* Value to enable */, "0" /* Value to disable */,
R.string.color_correction_feature_name));
featuresMap.put(ONE_HANDED_COMPONENT_NAME,
new ToggleableFrameworkFeatureInfo(
Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
"1" /* Value to enable */, "0" /* Value to disable */,
R.string.one_handed_mode_feature_name));
featuresMap.put(REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
new ToggleableFrameworkFeatureInfo(
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,

View File

@@ -21,6 +21,7 @@ import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTT
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
@@ -230,6 +231,16 @@ public final class AccessibilityTargetHelper {
context.getDrawable(R.drawable.ic_accessibility_color_inversion),
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
final ToggleAllowListingFeatureTarget oneHandedMode =
new ToggleAllowListingFeatureTarget(context,
shortcutType,
isShortcutContained(context, shortcutType,
ONE_HANDED_COMPONENT_NAME.flattenToString()),
ONE_HANDED_COMPONENT_NAME.flattenToString(),
context.getString(R.string.one_handed_mode_feature_name),
context.getDrawable(R.drawable.ic_accessibility_one_handed),
Settings.Secure.ONE_HANDED_MODE_ACTIVATED);
final ToggleAllowListingFeatureTarget reduceBrightColors =
new ToggleAllowListingFeatureTarget(context,
shortcutType,
@@ -243,6 +254,7 @@ public final class AccessibilityTargetHelper {
targets.add(magnification);
targets.add(daltonizer);
targets.add(colorInversion);
targets.add(oneHandedMode);
targets.add(reduceBrightColors);
return targets;

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/accessibility_feature_background" />
<foreground>
<inset
android:drawable="@drawable/ic_accessibility_one_handed_foreground"
android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
</foreground>
</adaptive-icon>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@android:color/white"
android:pathData="M4.64169 3C3.88567 3 3.27271 3.61296 3.27271 4.36898V18.4011C3.27271 19.154 3.88875 19.7701 4.64169 19.7701H12.5339C12.5339 19.7701 12.5425 18.0588 11.2324 18.0588H6.01067C5.44597 18.0588 4.98393 17.5968 4.98393 17.0321V5.73797C4.98393 5.17326 5.44597 4.71123 6.01067 4.71123H15.9358C16.5005 4.71123 16.9625 5.17326 16.9625 5.73797V9.84492C16.9625 10.9651 16.4899 12.5952 15.5936 13.2674L9.77538 9.16043C8.58505 10.425 8.88177 11.705 10.1176 12.9251L13.1978 16.0053C13.1978 16.0053 13.3231 17.4572 13.5401 18.0588C14.2034 19.8984 16.2781 20.7968 16.2781 20.7968H19.231H19.2967C20.0835 20.7968 20.7273 20.153 20.7273 19.3662V12.2543L18.9441 4.06781C18.7662 3.23718 18.4068 3 17.8729 3H4.64169Z"/>
</vector>

View File

@@ -4636,6 +4636,9 @@
shown in the warning dialog about the accessibility shortcut. -->
<string name="color_correction_feature_name">Color Correction</string>
<!-- Title of One Handed Mode feature, shown in the system gestures of the accessibility shortcut. [CHAR LIMIT=none] -->
<string name="one_handed_mode_feature_name">One-Handed mode</string>
<!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
<string name="reduce_bright_colors_feature_name">Extra dim</string>

View File

@@ -3410,9 +3410,12 @@
<java-symbol type="drawable" name="ic_accessibility_color_correction" />
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
<java-symbol type="drawable" name="ic_accessibility_one_handed" />
<java-symbol type="string" name="reduce_bright_colors_feature_name" />
<java-symbol type="string" name="one_handed_mode_feature_name" />
<!-- com.android.internal.widget.RecyclerView -->
<java-symbol type="id" name="item_touch_helper_previous_elevation"/>
<java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>

View File

@@ -135,6 +135,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
};
private final ContentObserver mActivatedObserver;
private final ContentObserver mEnabledObserver;
private final ContentObserver mTimeoutObserver;
private final ContentObserver mTaskChangeExitObserver;
@@ -170,11 +171,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
@Override
public void onStartFinished(Rect bounds) {
mState.setState(STATE_ACTIVE);
notifyShortcutState(STATE_ACTIVE);
}
@Override
public void onStopFinished(Rect bounds) {
mState.setState(STATE_NONE);
notifyShortcutState(STATE_NONE);
}
};
@@ -287,6 +290,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
context.getContentResolver(), mUserId);
mTimeoutHandler = timeoutHandler;
mActivatedObserver = getObserver(this::onActivatedActionChanged);
mEnabledObserver = getObserver(this::onEnabledSettingChanged);
mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
@@ -347,6 +351,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
updateOneHandedEnabled();
}
@VisibleForTesting
void notifyShortcutState(@OneHandedState.State int state) {
mOneHandedSettingsUtil.setOneHandedModeActivated(
mContext.getContentResolver(), state == STATE_ACTIVE ? 1 : 0, mUserId);
}
@VisibleForTesting
void startOneHanded() {
if (isLockedDisabled()) {
@@ -416,6 +426,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
private void registerSettingObservers(int newUserId) {
mOneHandedSettingsUtil.registerSettingsKeyObserver(
Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
mContext.getContentResolver(), mActivatedObserver, newUserId);
mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
mContext.getContentResolver(), mEnabledObserver, newUserId);
mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
@@ -465,6 +478,26 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
};
}
@VisibleForTesting
void onActivatedActionChanged() {
if (mState.isTransitioning() || !isOneHandedEnabled()) {
return;
}
final boolean isActivated = mState.getState() == STATE_ACTIVE;
final boolean requestActivated = mOneHandedSettingsUtil.getOneHandedModeActivated(
mContext.getContentResolver(), mUserId);
// When gesture trigger action, we will update settings and introduce observer callback
// again, then the following logic will just ignore the second redundant callback.
if (isActivated ^ requestActivated) {
if (requestActivated) {
startOneHanded();
} else {
stopOneHanded();
}
}
}
@VisibleForTesting
void onEnabledSettingChanged() {
final boolean enabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
@@ -544,6 +577,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
return mLockedDisabled;
}
@VisibleForTesting
boolean isOneHandedEnabled() {
return mIsOneHandedEnabled;
}
private void updateOneHandedEnabled() {
if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
mMainExecutor.execute(() -> stopOneHanded());

View File

@@ -131,8 +131,28 @@ public final class OneHandedSettingsUtil {
* Returns whether swipe bottom to notification gesture enabled or not.
*/
public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver, int userId) {
return Settings.Secure.getInt(resolver,
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0 /* Default OFF */) == 1;
return Settings.Secure.getIntForUser(resolver,
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0, userId) == 1;
}
/**
* Sets one handed activated or not to notify state for shortcut
*
* @return activated or not
*/
public boolean getOneHandedModeActivated(ContentResolver resolver, int userId) {
return Settings.Secure.getIntForUser(resolver,
Settings.Secure.ONE_HANDED_MODE_ACTIVATED, 0, userId) == 1;
}
/**
* Sets one handed activated or not to notify state for shortcut
*
* @return activated or not
*/
public boolean setOneHandedModeActivated(ContentResolver resolver, int state, int userId) {
return Settings.Secure.putIntForUser(resolver,
Settings.Secure.ONE_HANDED_MODE_ACTIVATED, state, userId);
}
void dump(PrintWriter pw, String prefix, ContentResolver resolver,
@@ -145,6 +165,8 @@ public final class OneHandedSettingsUtil {
pw.println(getSettingsOneHandedModeTimeout(resolver, userId));
pw.print(innerPrefix + "tapsAppToExit=");
pw.println(getSettingsTapsAppToExit(resolver, userId));
pw.print(innerPrefix + "shortcutActivated=");
pw.println(getOneHandedModeActivated(resolver, userId));
}
public OneHandedSettingsUtil() {

View File

@@ -16,7 +16,9 @@
package com.android.wm.shell.onehanded;
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 static com.google.common.truth.Truth.assertThat;
@@ -360,4 +362,71 @@ public class OneHandedControllerTest extends OneHandedTestCase {
verify(mMockGestureHandler).onGestureEnabled(isOneHandedEnabled);
}
@Test
public void testStateActive_shortcutRequestActivate_skipActions() {
when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController, never()).startOneHanded();
verify(mSpiedOneHandedController, never()).stopOneHanded();
}
@Test
public void testStateNotActive_shortcutRequestInActivate_skipAction() {
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController, never()).startOneHanded();
verify(mSpiedOneHandedController, never()).stopOneHanded();
}
@Test
public void testStateNotActive_shortcutRequestActivate_doAction() {
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController).startOneHanded();
verify(mSpiedOneHandedController, never()).stopOneHanded();
}
@Test
public void testEnteringTransition_shortcutRequestActivate_skipActions() {
when(mSpiedTransitionState.getState()).thenReturn(STATE_ENTERING);
when(mSpiedTransitionState.isTransitioning()).thenReturn(true);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController, never()).startOneHanded();
verify(mSpiedOneHandedController, never()).stopOneHanded();
}
@Test
public void testExitingTransition_shortcutRequestActivate_skipActions() {
when(mSpiedTransitionState.getState()).thenReturn(STATE_EXITING);
when(mSpiedTransitionState.isTransitioning()).thenReturn(true);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController, never()).startOneHanded();
verify(mSpiedOneHandedController, never()).stopOneHanded();
}
@Test
public void testOneHandedDisabled_shortcutEnabled_skipActions() {
when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController, never()).startOneHanded();
verify(mSpiedOneHandedController, never()).stopOneHanded();
}
}