Update accesibility button to suuport accessibility gesutre

1. Add gesture preference controller for gesture navigation
2. Auto update the preview illustarion when screen reader is on
3. Auto update accessibility button title for system navigation

Bug: 190563948
Test: make RunSettingsRoboTests ROBOTEST_FILTER=AccessibilityButtonGesturePreferenceControllerTest ROBOTEST_FILTER=AccessibilityButtonPreviewPreferenceControllerTest ROBOTEST_FILTER=AccessibilityLayerDrawableTest ROBOTEST_FILTER=AccessibilityPreferenceControllerTest ROBOTEST_FILTER=AccessibilityButtonFooterPreferenceControllerTest
Change-Id: Ifa98fc029430d86d3143133b3203b239340f2e41
This commit is contained in:
menghanli
2021-06-16 08:41:13 +08:00
parent 7f907d35cd
commit c472d0277f
17 changed files with 505 additions and 64 deletions

View File

@@ -47,13 +47,12 @@ public class AccessibilityButtonFooterPreferenceController extends
public void displayPreference(PreferenceScreen screen) {
// Need to update footerPreference's data before super.displayPreference(), then it will use
// data to update related property of footerPreference.
if (AccessibilityUtil.isGestureNavigateEnabled(mContext)) {
final AccessibilityFooterPreference footerPreference =
screen.findPreference(getPreferenceKey());
footerPreference.setTitle(
mContext.getString(R.string.accessibility_button_gesture_description));
}
final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_description
: R.string.accessibility_button_description;
final AccessibilityFooterPreference footerPreference =
screen.findPreference(getPreferenceKey());
footerPreference.setTitle(titleResource);
super.displayPreference(screen);
}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.accessibility;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
@@ -29,6 +30,14 @@ public class AccessibilityButtonFragment extends DashboardFragment {
private static final String TAG = "AccessibilityButtonFragment";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(getPrefContext())
? R.string.accessibility_button_gesture_title : R.string.accessibility_button_title;
getActivity().setTitle(titleResource);
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_button_settings;

View File

@@ -0,0 +1,82 @@
/*
* 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.settings.accessibility;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.google.common.primitives.Ints;
import java.util.Optional;
/** Preference controller that controls the button or gesture in accessibility button page. */
public class AccessibilityButtonGesturePreferenceController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener {
private Optional<Integer> mDefaultGesture = Optional.empty();
public AccessibilityButtonGesturePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AccessibilityUtil.isGestureNavigateEnabled(mContext)
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final ListPreference listPreference = (ListPreference) preference;
final Integer value = Ints.tryParse((String) newValue);
if (value != null) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, value);
updateState(listPreference);
}
return true;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
final ListPreference listPreference = (ListPreference) preference;
listPreference.setValue(getCurrentAccessibilityButtonMode());
}
private String getCurrentAccessibilityButtonMode() {
final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, getDefaultGestureValue());
return String.valueOf(mode);
}
private int getDefaultGestureValue() {
if (!mDefaultGesture.isPresent()) {
final String[] valuesList = mContext.getResources().getStringArray(
R.array.accessibility_button_gesture_selector_values);
mDefaultGesture = Optional.of(Integer.parseInt(valuesList[0]));
}
return mDefaultGesture.get();
}
}

View File

@@ -18,7 +18,6 @@ package com.android.settings.accessibility;
import android.content.Context;
import android.provider.Settings;
import android.util.ArrayMap;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
@@ -28,16 +27,16 @@ import com.android.settings.core.BasePreferenceController;
import com.google.common.primitives.Ints;
import java.util.Optional;
/** Preference controller that controls the preferred location in accessibility button page. */
public class AccessibilityButtonLocationPreferenceController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener {
private final ArrayMap<String, String> mValueTitleMap = new ArrayMap<>();
private int mDefaultLocation;
private Optional<Integer> mDefaultLocation = Optional.empty();
public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
initValueTitleMap();
}
@Override
@@ -68,22 +67,16 @@ public class AccessibilityButtonLocationPreferenceController extends BasePrefere
private String getCurrentAccessibilityButtonMode() {
final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mDefaultLocation);
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, getDefaultLocationValue());
return String.valueOf(mode);
}
private void initValueTitleMap() {
if (mValueTitleMap.size() == 0) {
final String[] values = mContext.getResources().getStringArray(
private int getDefaultLocationValue() {
if (!mDefaultLocation.isPresent()) {
final String[] valuesList = mContext.getResources().getStringArray(
R.array.accessibility_button_location_selector_values);
final String[] titles = mContext.getResources().getStringArray(
R.array.accessibility_button_location_selector_titles);
final int mapSize = values.length;
mDefaultLocation = Integer.parseInt(values[0]);
for (int i = 0; i < mapSize; i++) {
mValueTitleMap.put(values[i], titles[i]);
}
mDefaultLocation = Optional.of(Integer.parseInt(valuesList[0]));
}
return mDefaultLocation.get();
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.settings.accessibility;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
/**
* Preference controller for accessibility button preference.
*/
public class AccessibilityButtonPreferenceController extends BasePreferenceController {
public AccessibilityButtonPreferenceController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_title : R.string.accessibility_button_title;
final Preference preference = screen.findPreference(getPreferenceKey());
preference.setTitle(titleResource);
}
}

View File

@@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import android.widget.ImageView;
import androidx.annotation.VisibleForTesting;
@@ -46,11 +47,14 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
private final ContentResolver mContentResolver;
@VisibleForTesting
final ContentObserver mContentObserver;
private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable;
private AccessibilityLayerDrawable mAccessibilityPreviewDrawable;
@VisibleForTesting
ImageView mPreview;
private AccessibilityManager.TouchExplorationStateChangeListener
mTouchExplorationStateChangeListener;
public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mContentResolver = context.getContentResolver();
@@ -60,6 +64,9 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
updatePreviewPreference();
}
};
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
updatePreviewPreference();
};
}
@Override
@@ -78,6 +85,9 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
@Override
public void onResume() {
final AccessibilityManager am = mContext.getSystemService(AccessibilityManager.class);
am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
/* notifyForDescendants= */ false, mContentObserver);
@@ -91,6 +101,9 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
@Override
public void onPause() {
final AccessibilityManager am = mContext.getSystemService(AccessibilityManager.class);
am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
mContentResolver.unregisterContentObserver(mContentObserver);
}
@@ -103,9 +116,14 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
final int floatingMenuIconId = (size == SMALL_SIZE)
? R.drawable.accessibility_button_preview_small_floating_menu
: R.drawable.accessibility_button_preview_large_floating_menu;
mPreview.setImageDrawable(getFloatingMenuPreviewDrawable(floatingMenuIconId, opacity));
// Only change opacity(alpha) would not invoke redraw view, need to invalidate manually.
mPreview.setImageDrawable(getAccessibilityPreviewDrawable(floatingMenuIconId, opacity));
mPreview.invalidate();
} else if (AccessibilityUtil.isGestureNavigateEnabled(mContext)) {
// TODO(b/193081959): Use static illustartion instead.
final int resId = AccessibilityUtil.isTouchExploreEnabled(mContext)
? R.drawable.accessibility_button_preview_three_finger
: R.drawable.accessibility_button_preview_two_finger;
mPreview.setImageDrawable(getAccessibilityPreviewDrawable(resId, /* opacity= */ 100));
mPreview.invalidate();
} else {
mPreview.setImageDrawable(
@@ -113,14 +131,14 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
}
}
private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) {
if (mFloatingMenuPreviewDrawable == null) {
mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable(
private Drawable getAccessibilityPreviewDrawable(int resId, int opacity) {
if (mAccessibilityPreviewDrawable == null) {
mAccessibilityPreviewDrawable = AccessibilityLayerDrawable.createLayerDrawable(
mContext, resId, opacity);
} else {
mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
mAccessibilityPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
}
return mFloatingMenuPreviewDrawable;
return mAccessibilityPreviewDrawable;
}
}

View File

@@ -27,10 +27,10 @@ import com.android.settings.R;
import java.util.Objects;
/** LayerDrawable that contains device icon as background and floating menu icon as foreground. */
public class FloatingMenuLayerDrawable extends LayerDrawable {
/** LayerDrawable that contains device icon as background and given icon as foreground. */
public class AccessibilityLayerDrawable extends LayerDrawable {
private FloatingMenuLayerDrawableState mState;
private AccessibilityLayerDrawableState mState;
/**
* Creates a new layer drawable with the list of specified layers.
@@ -38,23 +38,23 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
* @param layers a list of drawables to use as layers in this new drawable,
* must be non-null
*/
private FloatingMenuLayerDrawable(@NonNull Drawable[] layers) {
private AccessibilityLayerDrawable(@NonNull Drawable[] layers) {
super(layers);
}
/**
* Create the {@link LayerDrawable} that contains device icon as background and floating menu
* icon with given {@code opacity} value as foreground.
* Create the {@link LayerDrawable} that contains device icon as background and given menu icon
* with given {@code opacity} value as foreground.
*
* @param context the valid context used to get the icon
* @param resId the resource ID of the floating menu icon
* @param resId the resource ID of the given icon
* @param opacity the opacity to apply to the given icon
* @return the drawable that combines the device icon and the floating menu icon
* @return the drawable that combines the device icon and the given icon
*/
public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId,
public static AccessibilityLayerDrawable createLayerDrawable(Context context, int resId,
int opacity) {
final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base);
final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable(
final AccessibilityLayerDrawable basicDrawable = new AccessibilityLayerDrawable(
new Drawable[]{bg, null});
basicDrawable.updateLayerDrawable(context, resId, opacity);
@@ -66,7 +66,7 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
* value at index 1 layer.
*
* @param context the valid context used to get the icon
* @param resId the resource ID of the floating menu icon
* @param resId the resource ID of the given icon
* @param opacity the opacity to apply to the given icon
*/
public void updateLayerDrawable(Context context, int resId, int opacity) {
@@ -83,18 +83,18 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
/** Stores the constant state and data to the given drawable. */
private void setConstantState(Context context, int resId, int opacity) {
mState = new FloatingMenuLayerDrawableState(context, resId, opacity);
mState = new AccessibilityLayerDrawableState(context, resId, opacity);
}
/** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */
/** {@link ConstantState} to store the data of {@link AccessibilityLayerDrawable}. */
@VisibleForTesting
static class FloatingMenuLayerDrawableState extends ConstantState {
static class AccessibilityLayerDrawableState extends ConstantState {
private final Context mContext;
private final int mResId;
private final int mOpacity;
FloatingMenuLayerDrawableState(Context context, int resId, int opacity) {
AccessibilityLayerDrawableState(Context context, int resId, int opacity) {
mContext = context;
mResId = resId;
mOpacity = opacity;
@@ -119,7 +119,7 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
if (o == null || getClass() != o.getClass()) {
return false;
}
final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o;
final AccessibilityLayerDrawableState that = (AccessibilityLayerDrawableState) o;
return mResId == that.mResId
&& mOpacity == that.mOpacity
&& Objects.equals(mContext, that.mContext);