Merge changes from topic "accessibility_floating_menu_ui" into sc-dev

* changes:
  Remove half circle option in size selector
  Update the vector drawable for accessibility button preview preference
  Remove the accessibility gesture tutorial dialog in system navigation
  Update the dialog content for accessibility floating menu
  Add the preference controller to control accessibility button fade preference
  Add the preference controller to control accessibility button preview preference
  Add the preference controller to control accessibility button opacity preference
  Add the preference controller to control accessibility button size preference
  Add the preference controller to control accessibility button location preference
  Setup basic layout and resources for the accessibility button settings page
This commit is contained in:
Jason Hsu
2021-03-18 04:54:54 +00:00
committed by Android (Google) Code Review
31 changed files with 2052 additions and 103 deletions

View File

@@ -0,0 +1,55 @@
/*
* 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.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
/** Settings fragment containing accessibility button properties. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class AccessibilityButtonFragment extends DashboardFragment {
private static final String TAG = "AccessibilityButtonFragment";
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_button_settings;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_BUTTON_SETTINGS;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_button_settings);
}

View File

@@ -0,0 +1,89 @@
/*
* 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 android.util.ArrayMap;
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;
/** 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;
public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
initValueTitleMap();
}
@Override
public int getAvailabilityStatus() {
return AccessibilityUtil.isGestureNavigateEnabled(mContext)
? DISABLED_DEPENDENT_SETTING : AVAILABLE;
}
@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, mDefaultLocation);
return String.valueOf(mode);
}
private void initValueTitleMap() {
if (mValueTitleMap.size() == 0) {
final String[] values = 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]);
}
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.widget.ImageView;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.widget.LayoutPreference;
/** Preference controller that controls the preview effect in accessibility button page. */
public class AccessibilityButtonPreviewPreferenceController extends BasePreferenceController
implements LifecycleObserver, OnResume, OnPause {
private static final int SMALL_SIZE = 0;
private static final float DEFAULT_OPACITY = 0.55f;
private static final int DEFAULT_SIZE = 0;
private final ContentResolver mContentResolver;
@VisibleForTesting
final ContentObserver mContentObserver;
private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable;
@VisibleForTesting
ImageView mPreview;
public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mContentResolver = context.getContentResolver();
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
updatePreviewPreference();
}
};
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final LayoutPreference preference = screen.findPreference(getPreferenceKey());
mPreview = preference.findViewById(R.id.preview_image);
updatePreviewPreference();
}
@Override
public void onResume() {
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
/* notifyForDescendants= */ false, mContentObserver);
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
/* notifyForDescendants= */ false, mContentObserver);
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
/* notifyForDescendants= */ false, mContentObserver);
}
@Override
public void onPause() {
mContentResolver.unregisterContentObserver(mContentObserver);
}
private void updatePreviewPreference() {
if (AccessibilityUtil.isFloatingMenuEnabled(mContext)) {
final int size = Settings.Secure.getInt(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, DEFAULT_SIZE);
final int opacity = (int) (Settings.Secure.getFloat(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY) * 100);
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.invalidate();
} else {
mPreview.setImageDrawable(
mContext.getDrawable(R.drawable.accessibility_button_navigation));
}
}
private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) {
if (mFloatingMenuPreviewDrawable == null) {
mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable(
mContext, resId, opacity);
} else {
mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
}
return mFloatingMenuPreviewDrawable;
}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.accessibility;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
@@ -24,6 +25,7 @@ import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ImageSpan;
import android.view.LayoutInflater;
import android.view.View;
@@ -40,6 +42,8 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.utils.AnnotationSpan;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -253,6 +257,8 @@ public class AccessibilityEditDialogUtils {
summary.setVisibility(View.GONE);
} else {
summary.setText(summaryText);
summary.setMovementMethod(LinkMovementMethod.getInstance());
summary.setFocusable(false);
}
final ImageView image = view.findViewById(R.id.image);
image.setImageResource(imageResId);
@@ -260,10 +266,13 @@ public class AccessibilityEditDialogUtils {
private static void initSoftwareShortcut(Context context, View view) {
final View dialogView = view.findViewById(R.id.software_shortcut);
final CharSequence title = context.getText(
R.string.accessibility_shortcut_edit_dialog_title_software);
final TextView summary = dialogView.findViewById(R.id.summary);
final int lineHeight = summary.getLineHeight();
setupShortcutWidget(dialogView, retrieveTitle(context),
retrieveSummary(context, lineHeight), retrieveImageResId(context));
setupShortcutWidget(dialogView, title, retrieveSummary(context, lineHeight),
retrieveImageResId(context));
}
private static void initHardwareShortcut(Context context, View view) {
@@ -297,35 +306,28 @@ public class AccessibilityEditDialogUtils {
});
}
private static CharSequence retrieveTitle(Context context) {
int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
: R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
}
return context.getText(resId);
}
private static CharSequence retrieveSummary(Context context, int lineHeight) {
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback
: R.string.accessibility_shortcut_edit_dialog_summary_software_gesture;
return context.getText(resId);
}
return getSummaryStringWithIcon(context, lineHeight);
return AccessibilityUtil.isFloatingMenuEnabled(context)
? getSummaryStringWithLink(context) : getSummaryStringWithIcon(context, lineHeight);
}
private static int retrieveImageResId(Context context) {
// TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
int resId = R.drawable.accessibility_shortcut_type_software;
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.drawable.accessibility_shortcut_type_software_gesture_talkback
: R.drawable.accessibility_shortcut_type_software_gesture;
}
return resId;
return AccessibilityUtil.isFloatingMenuEnabled(context)
? R.drawable.accessibility_shortcut_type_software_floating
: R.drawable.accessibility_shortcut_type_software;
}
private static CharSequence getSummaryStringWithLink(Context context) {
final View.OnClickListener linkListener = v -> new SubSettingLauncher(context)
.setDestination(AccessibilityButtonFragment.class.getName())
.setSourceMetricsCategory(
SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS)
.launch();
final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, linkListener);
return AnnotationSpan.linkify(context.getText(
R.string.accessibility_shortcut_edit_dialog_summary_software_floating), linkInfo);
}
private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {

View File

@@ -333,7 +333,8 @@ public final class AccessibilityGestureNavigationTutorial {
}
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
final CharSequence title = getSoftwareTitle(context);
final CharSequence title = context.getText(
R.string.accessibility_tutorial_dialog_title_button);
final ImageView image = createSoftwareImage(context);
final CharSequence instruction = getSoftwareInstruction(context);
final ImageView indicatorIcon =
@@ -390,44 +391,19 @@ public final class AccessibilityGestureNavigationTutorial {
return tutorialPages;
}
private static CharSequence getSoftwareTitle(Context context) {
final boolean isGestureNavigationEnabled =
AccessibilityUtil.isGestureNavigateEnabled(context);
final int resId = isGestureNavigationEnabled
? R.string.accessibility_tutorial_dialog_title_gesture
: R.string.accessibility_tutorial_dialog_title_button;
return context.getText(resId);
}
private static ImageView createSoftwareImage(Context context) {
int resId = R.drawable.accessibility_shortcut_type_software;
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.drawable.accessibility_shortcut_type_software_gesture_talkback
: R.drawable.accessibility_shortcut_type_software_gesture;
}
final int resId = AccessibilityUtil.isFloatingMenuEnabled(context)
? R.drawable.accessibility_shortcut_type_software_floating
: R.drawable.accessibility_shortcut_type_software;
return createImageView(context, resId);
}
private static CharSequence getSoftwareInstruction(Context context) {
final boolean isGestureNavigateEnabled =
AccessibilityUtil.isGestureNavigateEnabled(context);
final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context);
int resId = R.string.accessibility_tutorial_dialog_message_button;
if (isGestureNavigateEnabled) {
resId = isTouchExploreEnabled
? R.string.accessibility_tutorial_dialog_message_gesture_talkback
: R.string.accessibility_tutorial_dialog_message_gesture;
}
CharSequence text = context.getText(resId);
if (resId == R.string.accessibility_tutorial_dialog_message_button) {
text = getSoftwareInstructionWithIcon(context, text);
}
return text;
return AccessibilityUtil.isFloatingMenuEnabled(context)
? context.getText(R.string.accessibility_tutorial_dialog_message_floating_button)
: getSoftwareInstructionWithIcon(context,
context.getText(R.string.accessibility_tutorial_dialog_message_button));
}
private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {

View File

@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -143,6 +144,13 @@ final class AccessibilityUtil {
== NAV_BAR_MODE_GESTURAL;
}
/** Determines if a accessibility floating menu is being used. */
public static boolean isFloatingMenuEnabled(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
== ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
}
/** Determines if a touch explore is being used. */
public static boolean isTouchExploreEnabled(Context context) {
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);

View File

@@ -0,0 +1,116 @@
/*
* 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.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
/** Preference controller that controls the fade switch button in accessibility button page. */
public class FloatingMenuFadePreferenceController extends BasePreferenceController implements
Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
private static final int OFF = 0;
private static final int ON = 1;
private final ContentResolver mContentResolver;
@VisibleForTesting
final ContentObserver mContentObserver;
@VisibleForTesting
SwitchPreference mPreference;
public FloatingMenuFadePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mContentResolver = context.getContentResolver();
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
updateAvailabilityStatus();
}
};
}
@Override
public int getAvailabilityStatus() {
return AccessibilityUtil.isFloatingMenuEnabled(mContext)
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean isEnabled = (boolean) newValue;
putFloatingMenuFadeValue(isEnabled);
return true;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
final SwitchPreference switchPreference = (SwitchPreference) preference;
switchPreference.setChecked(getFloatingMenuFadeValue() == ON);
}
@Override
public void onResume() {
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
/* notifyForDescendants= */ false, mContentObserver);
}
@Override
public void onPause() {
mContentResolver.unregisterContentObserver(mContentObserver);
}
private void updateAvailabilityStatus() {
mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
}
private int getFloatingMenuFadeValue() {
return Settings.Secure.getInt(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, ON);
}
private void putFloatingMenuFadeValue(boolean isEnabled) {
Settings.Secure.putInt(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
isEnabled ? ON : OFF);
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
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 {
private FloatingMenuLayerDrawableState mState;
/**
* Creates a new layer drawable with the list of specified layers.
*
* @param layers a list of drawables to use as layers in this new drawable,
* must be non-null
*/
private FloatingMenuLayerDrawable(@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.
*
* @param context the valid context used to get the icon
* @param resId the resource ID of the floating menu icon
* @param opacity the opacity to apply to the given icon
* @return the drawable that combines the device icon and the floating menu icon
*/
public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId,
int opacity) {
final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base);
final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable(
new Drawable[]{bg, null});
basicDrawable.updateLayerDrawable(context, resId, opacity);
return basicDrawable;
}
/**
* Update the drawable with given {@code resId} drawable and {@code opacity}(alpha)
* 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 opacity the opacity to apply to the given icon
*/
public void updateLayerDrawable(Context context, int resId, int opacity) {
final Drawable icon = context.getDrawable(resId);
icon.setAlpha(opacity);
this.setDrawable(/* index= */ 1, icon);
this.setConstantState(context, resId, opacity);
}
@Override
public ConstantState getConstantState() {
return mState;
}
/** 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);
}
/** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */
@VisibleForTesting
static class FloatingMenuLayerDrawableState extends ConstantState {
private final Context mContext;
private final int mResId;
private final int mOpacity;
FloatingMenuLayerDrawableState(Context context, int resId, int opacity) {
mContext = context;
mResId = resId;
mOpacity = opacity;
}
@NonNull
@Override
public Drawable newDrawable() {
return createLayerDrawable(mContext, mResId, mOpacity);
}
@Override
public int getChangingConfigurations() {
return 0;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o;
return mResId == that.mResId
&& mOpacity == that.mOpacity
&& Objects.equals(mContext, that.mContext);
}
@Override
public int hashCode() {
return Objects.hash(mContext, mResId, mOpacity);
}
}
}

View File

@@ -0,0 +1,150 @@
/*
* 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.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.SliderPreferenceController;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
/** Preference controller that controls the opacity seekbar in accessibility button page. */
public class FloatingMenuOpacityPreferenceController extends SliderPreferenceController
implements LifecycleObserver, OnResume, OnPause {
@VisibleForTesting
static final float DEFAULT_OPACITY = 0.55f;
private static final int FADE_ENABLED = 1;
private static final float MIN_PROGRESS = 10f;
private static final float MAX_PROGRESS = 100f;
@VisibleForTesting
static final float PRECISION = 100f;
private final ContentResolver mContentResolver;
@VisibleForTesting
final ContentObserver mContentObserver;
@VisibleForTesting
SeekBarPreference mPreference;
public FloatingMenuOpacityPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey);
mContentResolver = context.getContentResolver();
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
updateAvailabilityStatus();
}
};
}
@Override
public int getAvailabilityStatus() {
return AccessibilityUtil.isFloatingMenuEnabled(mContext)
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
mPreference.setContinuousUpdates(true);
mPreference.setMax(getMax());
mPreference.setMin(getMin());
mPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
updateState(mPreference);
}
@Override
public void onResume() {
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
false, mContentObserver);
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
/* notifyForDescendants= */ false, mContentObserver);
}
@Override
public void onPause() {
mContentResolver.unregisterContentObserver(mContentObserver);
}
@Override
public int getSliderPosition() {
return convertOpacityFloatToInt(getOpacity());
}
@Override
public boolean setSliderPosition(int position) {
final float value = convertOpacityIntToFloat(position);
return Settings.Secure.putFloat(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, value);
}
@Override
public int getMax() {
return (int) MAX_PROGRESS;
}
@Override
public int getMin() {
return (int) MIN_PROGRESS;
}
private void updateAvailabilityStatus() {
final boolean fadeEnabled = Settings.Secure.getInt(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, FADE_ENABLED)
== FADE_ENABLED;
mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext) && fadeEnabled);
}
private int convertOpacityFloatToInt(float value) {
return Math.round(value * PRECISION);
}
private float convertOpacityIntToFloat(int value) {
return (float) value / PRECISION;
}
private float getOpacity() {
float value = Settings.Secure.getFloat(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY);
final float minValue = MIN_PROGRESS / PRECISION;
final float maxValue = MAX_PROGRESS / PRECISION;
return (value < minValue || value > maxValue) ? DEFAULT_OPACITY : value;
}
}

View File

@@ -0,0 +1,157 @@
/*
* 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.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.util.ArrayMap;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.google.common.primitives.Ints;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** Preference controller that controls the preferred size in accessibility button page. */
public class FloatingMenuSizePreferenceController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
private final ContentResolver mContentResolver;
@VisibleForTesting
final ContentObserver mContentObserver;
@VisibleForTesting
ListPreference mPreference;
private final ArrayMap<String, String> mValueTitleMap = new ArrayMap<>();
private int mDefaultSize;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
Size.SMALL,
Size.LARGE,
})
@VisibleForTesting
@interface Size {
int SMALL = 0;
int LARGE = 1;
}
public FloatingMenuSizePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mContentResolver = context.getContentResolver();
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
updateAvailabilityStatus();
}
};
initValueTitleMap();
}
@Override
public int getAvailabilityStatus() {
return AccessibilityUtil.isFloatingMenuEnabled(mContext)
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final ListPreference listPreference = (ListPreference) preference;
final Integer value = Ints.tryParse((String) newValue);
if (value != null) {
putAccessibilityFloatingMenuSize(value);
updateState(listPreference);
}
return true;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
final ListPreference listPreference = (ListPreference) preference;
listPreference.setValue(String.valueOf(getAccessibilityFloatingMenuSize(mDefaultSize)));
}
@Override
public void onResume() {
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
false, mContentObserver);
}
@Override
public void onPause() {
mContentResolver.unregisterContentObserver(mContentObserver);
}
private void updateAvailabilityStatus() {
mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
}
private void initValueTitleMap() {
if (mValueTitleMap.size() == 0) {
final String[] values = mContext.getResources().getStringArray(
R.array.accessibility_button_size_selector_values);
final String[] titles = mContext.getResources().getStringArray(
R.array.accessibility_button_size_selector_titles);
final int mapSize = values.length;
mDefaultSize = Integer.parseInt(values[0]);
for (int i = 0; i < mapSize; i++) {
mValueTitleMap.put(values[i], titles[i]);
}
}
}
@Size
private int getAccessibilityFloatingMenuSize(@Size int defaultValue) {
return Settings.Secure.getInt(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultValue);
}
private void putAccessibilityFloatingMenuSize(@Size int value) {
Settings.Secure.putInt(mContentResolver,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, value);
}
}

View File

@@ -608,19 +608,15 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
int resId = R.string.accessibility_shortcut_edit_summary_software;
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
: R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
}
final CharSequence softwareTitle = context.getText(resId);
List<CharSequence> list = new ArrayList<>();
if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
final List<CharSequence> list = new ArrayList<>();
final CharSequence softwareTitle = context.getText(
R.string.accessibility_shortcut_edit_summary_software);
if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
list.add(softwareTitle);
}
if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);

View File

@@ -226,27 +226,23 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return context.getText(R.string.switch_off_text);
}
final int shortcutType = PreferredShortcuts.retrieveUserShortcutType(context,
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE);
int resId = R.string.accessibility_shortcut_edit_summary_software;
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
: R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
}
final CharSequence softwareTitle = context.getText(resId);
List<CharSequence> list = new ArrayList<>();
if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
final List<CharSequence> list = new ArrayList<>();
final CharSequence softwareTitle = context.getText(
R.string.accessibility_shortcut_edit_summary_software);
if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
list.add(softwareTitle);
}
if ((shortcutType & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
if ((shortcutType & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) {
if (hasShortcutType(shortcutTypes, UserShortcutType.TRIPLETAP)) {
final CharSequence tripleTapTitle = context.getText(
R.string.accessibility_shortcut_triple_tap_keyword);
list.add(tripleTapTitle);

View File

@@ -30,15 +30,12 @@ import android.content.om.OverlayInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.SettingsTutorialDialogWrapperActivity;
import com.android.settings.core.FeatureFlags;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -188,12 +185,7 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i
protected boolean setDefaultKey(String key) {
setCurrentSystemNavigationMode(mOverlayManager, key);
setIllustrationVideo(mVideoPreference, key);
if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, key) && (
isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
Intent intent = new Intent(getActivity(), SettingsTutorialDialogWrapperActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
return true;
}
@@ -267,18 +259,6 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i
}
}
private boolean isAnyServiceSupportAccessibilityButton() {
final AccessibilityManager ams = getContext().getSystemService(AccessibilityManager.class);
final List<String> targets = ams.getAccessibilityShortcutTargets(
AccessibilityManager.ACCESSIBILITY_BUTTON);
return !targets.isEmpty();
}
private boolean isNavBarMagnificationEnabled() {
return Settings.Secure.getInt(getContext().getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.system_navigation_gesture_settings) {