Files
app_Settings/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
Chun-Ku Lin f5cc0119ee workaround(expressive): Fix padding issues Setup > Vision settings
- Works around issue where Setup and Settings libraries are clashing
  when applying their new expressive themes at the same time.
- Updates illustration prefs to match parent width. Otherwise this new
  padding workaround causes the images to get clipped.

Fix: 390545391
Bug: 400479388
Test: manual only; see screenshots
Flag: EXEMPT using custom flagging from Setup ThemeHelper lib
Change-Id: Ie00cdf3e68a2ff7c40aedd04eb5bed0adaa9cf84
2025-03-14 14:00:04 -07:00

795 lines
32 KiB
Java

/*
* Copyright (C) 2013 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 static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.service.quicksettings.TileService;
import android.text.Html;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.actionbar.FeedbackMenuController;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.flags.Flags;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.IllustrationPreference;
import com.android.settingslib.widget.TopIntroPreference;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.util.ThemeHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Base class for accessibility fragments with toggle, shortcut, some helper functions
* and dialog management.
*/
public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
implements ShortcutPreference.OnClickCallback, OnCheckedChangeListener {
public static final String KEY_GENERAL_CATEGORY = "general_categories";
public static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
protected static final String KEY_TOP_INTRO_PREFERENCE = "top_intro";
protected static final String KEY_HTML_DESCRIPTION_PREFERENCE = "html_description";
protected static final String KEY_ANIMATED_IMAGE = "animated_image";
// For html description of accessibility service, must follow the rule, such as
// <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
private static final String IMG_PREFIX = "R.drawable.";
private static final String DRAWABLE_FOLDER = "drawable";
protected TopIntroPreference mTopIntroPreference;
protected SettingsMainSwitchPreference mToggleServiceSwitchPreference;
protected ShortcutPreference mShortcutPreference;
protected Preference mSettingsPreference;
@Nullable protected AccessibilityFooterPreference mHtmlFooterPreference;
protected AccessibilityFooterPreferenceController mFooterPreferenceController;
protected String mPreferenceKey;
protected Dialog mDialog;
protected CharSequence mSettingsTitle;
protected Intent mSettingsIntent;
// The mComponentName maybe null, such as Magnify
protected ComponentName mComponentName;
protected CharSequence mFeatureName;
protected Uri mImageUri;
protected CharSequence mHtmlDescription;
protected CharSequence mTopIntroTitle;
private CharSequence mDescription;
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
private AccessibilitySettingsContentObserver mSettingsContentObserver;
private ImageView mImageGetterCacheView;
protected final Html.ImageGetter mImageGetter = (String str) -> {
if (str != null && str.startsWith(IMG_PREFIX)) {
final String fileName = str.substring(IMG_PREFIX.length());
return getDrawableFromUri(Uri.parse(
ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
+ mComponentName.getPackageName() + "/" + DRAWABLE_FOLDER + "/"
+ fileName));
}
return null;
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onProcessArguments(getArguments());
final int resId = getPreferenceScreenResId();
if (resId <= 0) {
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
getPrefContext());
setPreferenceScreen(preferenceScreen);
}
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
registerKeysToObserverCallback(mSettingsContentObserver);
FeedbackMenuController.init(this, getFeedbackCategory());
}
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
final List<String> shortcutFeatureKeys = getShortcutFeatureSettingsKeys();
contentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> {
updateShortcutPreferenceData();
updateShortcutPreference();
});
}
protected List<String> getShortcutFeatureSettingsKeys() {
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
return shortcutFeatureKeys;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
initTopIntroPreference();
initAnimatedImagePreference();
initToggleServiceSwitchPreference();
initGeneralCategory();
initShortcutPreference();
initSettingsPreference();
initAppInfoPreference();
initHtmlTextPreference();
initFooterPreference();
installActionBarToggleSwitch();
updateToggleServiceTitle(mToggleServiceSwitchPreference);
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
};
updatePreferenceOrder();
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public Dialog onCreateDialog(int dialogId) {
switch (dialogId) {
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
if (isAnySetupWizard()) {
mDialog = AccessibilityShortcutsTutorial
.createAccessibilityTutorialDialogForSetupWizard(
getPrefContext(), getUserPreferredShortcutTypes(),
this::callOnTutorialDialogButtonClicked, mFeatureName);
} else {
mDialog = AccessibilityShortcutsTutorial
.createAccessibilityTutorialDialog(
getPrefContext(), getUserPreferredShortcutTypes(),
this::callOnTutorialDialogButtonClicked, mFeatureName);
}
mDialog.setCanceledOnTouchOutside(false);
return mDialog;
default:
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final SettingsActivity settingsActivity = (SettingsActivity) getActivity();
final SettingsMainSwitchBar switchBar = settingsActivity.getSwitchBar();
switchBar.hide();
writeConfigDefaultAccessibilityServiceIntoShortcutTargetServiceIfNeeded(getContext());
}
@Override
public void onResume() {
super.onResume();
final AccessibilityManager am = getPrefContext().getSystemService(
AccessibilityManager.class);
am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
mSettingsContentObserver.register(getContentResolver());
updateShortcutPreferenceData();
updateShortcutPreference();
}
@Override
public void onPause() {
final AccessibilityManager am = getPrefContext().getSystemService(
AccessibilityManager.class);
am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
mSettingsContentObserver.unregister(getContentResolver());
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
removeActionBarToggleSwitch();
}
@Override
public int getDialogMetricsCategory(int dialogId) {
switch (dialogId) {
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL;
default:
return SettingsEnums.ACTION_UNKNOWN;
}
}
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_SERVICE;
}
/**
* Returns the category of the feedback page.
*
* <p>By default, this method returns {@link SettingsEnums#PAGE_UNKNOWN}. This indicates that
* the feedback category is unknown, and the absence of a feedback menu.
*
* @return The feedback category, which is {@link SettingsEnums#PAGE_UNKNOWN} by default.
*/
protected int getFeedbackCategory() {
return SettingsEnums.PAGE_UNKNOWN;
}
@Override
public int getHelpResource() {
return 0;
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
onPreferenceToggled(mPreferenceKey, isChecked);
}
/**
* Returns the shortcut type list which has been checked by user.
*/
abstract int getUserShortcutTypes();
/** Returns the accessibility tile component name. */
abstract ComponentName getTileComponentName();
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
final CharSequence title =
getString(R.string.accessibility_service_primary_switch_title, mFeatureName);
switchPreference.setTitle(title);
}
protected String getUseServicePreferenceKey() {
return "use_service";
}
protected CharSequence getShortcutTitle() {
return getString(R.string.accessibility_shortcut_title, mFeatureName);
}
@VisibleForTesting
CharSequence getContentDescriptionForAnimatedIllustration() {
return getString(R.string.accessibility_illustration_content_description, mFeatureName);
}
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
}
protected void onInstallSwitchPreferenceToggleSwitch() {
// Implement this to set a checked listener.
updateSwitchBarToggleSwitch();
mToggleServiceSwitchPreference.addOnSwitchChangeListener(this);
}
protected void onRemoveSwitchPreferenceToggleSwitch() {
// Implement this to reset a checked listener.
}
protected void updateSwitchBarToggleSwitch() {
// Implement this to update the state of switch.
}
public void setTitle(String title) {
getActivity().setTitle(title);
}
protected void onProcessArguments(@Nullable Bundle arguments) {
if (arguments == null) {
return;
}
// Key.
mPreferenceKey = arguments.getString(AccessibilitySettings.EXTRA_PREFERENCE_KEY);
// Title.
if (arguments.containsKey(AccessibilitySettings.EXTRA_RESOLVE_INFO)) {
ResolveInfo info = arguments.getParcelable(AccessibilitySettings.EXTRA_RESOLVE_INFO);
getActivity().setTitle(info.loadLabel(getPackageManager()).toString());
} else if (arguments.containsKey(AccessibilitySettings.EXTRA_TITLE)) {
setTitle(arguments.getString(AccessibilitySettings.EXTRA_TITLE));
}
// Summary.
if (arguments.containsKey(AccessibilitySettings.EXTRA_SUMMARY)) {
mDescription = arguments.getCharSequence(AccessibilitySettings.EXTRA_SUMMARY);
}
// Settings html description.
if (arguments.containsKey(AccessibilitySettings.EXTRA_HTML_DESCRIPTION)) {
mHtmlDescription = arguments.getCharSequence(
AccessibilitySettings.EXTRA_HTML_DESCRIPTION);
}
// Intro.
if (arguments.containsKey(AccessibilitySettings.EXTRA_INTRO)) {
mTopIntroTitle = arguments.getCharSequence(AccessibilitySettings.EXTRA_INTRO);
}
}
private void installActionBarToggleSwitch() {
onInstallSwitchPreferenceToggleSwitch();
}
private void removeActionBarToggleSwitch() {
mToggleServiceSwitchPreference.setOnPreferenceClickListener(null);
onRemoveSwitchPreferenceToggleSwitch();
}
private void updatePreferenceOrder() {
final List<String> lists = getPreferenceOrderList();
final PreferenceScreen preferenceScreen = getPreferenceScreen();
preferenceScreen.setOrderingAsAdded(false);
final int size = lists.size();
for (int i = 0; i < size; i++) {
final Preference preference = preferenceScreen.findPreference(lists.get(i));
if (preference != null) {
preference.setOrder(i);
}
}
}
/** Customizes the order by preference key. */
protected List<String> getPreferenceOrderList() {
final List<String> lists = new ArrayList<>();
lists.add(KEY_TOP_INTRO_PREFERENCE);
lists.add(KEY_ANIMATED_IMAGE);
lists.add(getUseServicePreferenceKey());
lists.add(KEY_GENERAL_CATEGORY);
lists.add(KEY_HTML_DESCRIPTION_PREFERENCE);
return lists;
}
private Drawable getDrawableFromUri(Uri imageUri) {
if (mImageGetterCacheView == null) {
mImageGetterCacheView = new ImageView(getPrefContext());
}
mImageGetterCacheView.setAdjustViewBounds(true);
mImageGetterCacheView.setImageURI(imageUri);
if (mImageGetterCacheView.getDrawable() == null) {
return null;
}
final Drawable drawable =
mImageGetterCacheView.getDrawable().mutate().getConstantState().newDrawable();
mImageGetterCacheView.setImageURI(null);
final int imageWidth = drawable.getIntrinsicWidth();
final int imageHeight = drawable.getIntrinsicHeight();
final int screenHalfHeight = AccessibilityUtil.getScreenHeightPixels(getPrefContext()) / 2;
if ((imageWidth > AccessibilityUtil.getScreenWidthPixels(getPrefContext()))
|| (imageHeight > screenHalfHeight)) {
return null;
}
drawable.setBounds(/* left= */0, /* top= */0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
return drawable;
}
private void initAnimatedImagePreference() {
initAnimatedImagePreference(mImageUri, new IllustrationPreference(getPrefContext()) {
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
if (ThemeHelper.shouldApplyGlifExpressiveStyle(getContext())
&& isAnySetupWizard()) {
View illustrationFrame = holder.findViewById(R.id.illustration_frame);
final ViewGroup.LayoutParams lp = illustrationFrame.getLayoutParams();
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
illustrationFrame.setLayoutParams(lp);
}
}
});
}
@VisibleForTesting
void initAnimatedImagePreference(
@Nullable Uri imageUri,
@NonNull IllustrationPreference preference) {
if (imageUri == null) {
return;
}
final int displayHalfHeight =
AccessibilityUtil.getDisplayBounds(getPrefContext()).height() / 2;
preference.setImageUri(imageUri);
preference.setSelectable(false);
preference.setMaxHeight(displayHalfHeight);
preference.setKey(KEY_ANIMATED_IMAGE);
preference.setOnBindListener(view -> {
// isAnimatable is decided in
// {@link IllustrationPreference#onBindViewHolder(PreferenceViewHolder)}. Therefore, we
// wait until the view is bond to set the content description for it.
// The content description is added for an animation illustration only. Since the static
// images are decorative.
ThreadUtils.getUiThreadHandler().post(() -> {
if (preference.isAnimatable()) {
preference.setContentDescription(
getContentDescriptionForAnimatedIllustration());
}
});
});
getPreferenceScreen().addPreference(preference);
}
@VisibleForTesting
void initTopIntroPreference() {
if (TextUtils.isEmpty(mTopIntroTitle)) {
return;
}
mTopIntroPreference = new TopIntroPreference(getPrefContext());
mTopIntroPreference.setKey(KEY_TOP_INTRO_PREFERENCE);
mTopIntroPreference.setTitle(mTopIntroTitle);
getPreferenceScreen().addPreference(mTopIntroPreference);
}
private void initToggleServiceSwitchPreference() {
mToggleServiceSwitchPreference = new SettingsMainSwitchPreference(getPrefContext());
mToggleServiceSwitchPreference.setKey(getUseServicePreferenceKey());
if (getArguments() != null
&& getArguments().containsKey(AccessibilitySettings.EXTRA_CHECKED)) {
final boolean enabled = getArguments().getBoolean(AccessibilitySettings.EXTRA_CHECKED);
mToggleServiceSwitchPreference.setChecked(enabled);
}
getPreferenceScreen().addPreference(mToggleServiceSwitchPreference);
}
private void initGeneralCategory() {
final PreferenceCategory generalCategory = new PreferenceCategory(getPrefContext());
generalCategory.setKey(KEY_GENERAL_CATEGORY);
generalCategory.setTitle(R.string.accessibility_screen_option);
getPreferenceScreen().addPreference(generalCategory);
}
protected void initShortcutPreference() {
// Initial the shortcut preference.
mShortcutPreference = new ShortcutPreference(getPrefContext(), /* attrs= */ null);
mShortcutPreference.setPersistent(false);
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setOnClickCallback(this);
mShortcutPreference.setTitle(getShortcutTitle());
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mShortcutPreference);
}
protected void initSettingsPreference() {
if (mSettingsTitle == null || mSettingsIntent == null) {
return;
}
// Show the "Settings" menu as if it were a preference screen.
mSettingsPreference = new Preference(getPrefContext());
mSettingsPreference.setTitle(mSettingsTitle);
mSettingsPreference.setIconSpaceReserved(false);
mSettingsPreference.setIntent(mSettingsIntent);
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mSettingsPreference);
}
@VisibleForTesting
@Nullable
Preference createAppInfoPreference() {
if (!Flags.accessibilityShowAppInfoButton()) {
return null;
}
// App Info is not available in Setup Wizard.
if (isAnySetupWizard()) {
return null;
}
// Only show the button for pages with valid component package names.
if (mComponentName == null) {
return null;
}
final String packageName = mComponentName.getPackageName();
final PackageManager packageManager = getPrefContext().getPackageManager();
if (!packageManager.isPackageAvailable(packageName)) {
return null;
}
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setPackage(getContext().getPackageName());
intent.setData(Uri.parse("package:" + packageName));
final Preference appInfoPreference = new Preference(getPrefContext());
appInfoPreference.setTitle(getString(R.string.application_info_label));
appInfoPreference.setIconSpaceReserved(false);
appInfoPreference.setIntent(intent);
return appInfoPreference;
}
private void initAppInfoPreference() {
final Preference appInfoPreference = createAppInfoPreference();
if (appInfoPreference != null) {
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(appInfoPreference);
}
}
private void initHtmlTextPreference() {
if (TextUtils.isEmpty(getCurrentHtmlDescription())) {
return;
}
final PreferenceScreen screen = getPreferenceScreen();
mHtmlFooterPreference =
new AccessibilityFooterPreference(screen.getContext());
mHtmlFooterPreference.setKey(KEY_HTML_DESCRIPTION_PREFERENCE);
updateHtmlTextPreference();
screen.addPreference(mHtmlFooterPreference);
// TODO(b/171272809): Migrate to DashboardFragment.
final String title = getString(R.string.accessibility_introduction_title, mFeatureName);
mFooterPreferenceController = new AccessibilityFooterPreferenceController(
screen.getContext(), mHtmlFooterPreference.getKey());
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.displayPreference(screen);
}
protected void updateHtmlTextPreference() {
if (mHtmlFooterPreference == null) {
return;
}
String description = getCurrentHtmlDescription().toString();
final CharSequence htmlDescription = Html.fromHtml(description,
Html.FROM_HTML_MODE_COMPACT, mImageGetter, /* tagHandler= */ null);
mHtmlFooterPreference.setSummary(htmlDescription);
}
CharSequence getCurrentHtmlDescription() {
return mHtmlDescription;
}
private void initFooterPreference() {
if (!TextUtils.isEmpty(mDescription)) {
createFooterPreference(getPreferenceScreen(), mDescription,
getString(R.string.accessibility_introduction_title, mFeatureName));
}
}
/**
* Creates {@link AccessibilityFooterPreference} and append into {@link PreferenceScreen}
*
* @param screen The preference screen to add the footer preference
* @param summary The summary of the preference summary
* @param introductionTitle The title of introduction in the footer
*/
@VisibleForTesting
void createFooterPreference(PreferenceScreen screen, CharSequence summary,
String introductionTitle) {
final AccessibilityFooterPreference footerPreference =
new AccessibilityFooterPreference(screen.getContext());
footerPreference.setSummary(summary);
screen.addPreference(footerPreference);
mFooterPreferenceController = new AccessibilityFooterPreferenceController(
screen.getContext(), footerPreference.getKey());
mFooterPreferenceController.setIntroductionTitle(introductionTitle);
mFooterPreferenceController.displayPreference(screen);
}
protected CharSequence getShortcutTypeSummary(Context context) {
if (!mShortcutPreference.isSettingsEditable()) {
return context.getText(R.string.accessibility_shortcut_edit_dialog_title_hardware);
}
if (!mShortcutPreference.isChecked()) {
return context.getText(R.string.accessibility_shortcut_state_off);
}
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(
context, mComponentName.flattenToString(), getDefaultShortcutTypes());
return getShortcutSummaryList(context, shortcutTypes);
}
/**
* This method will be invoked when a button in the tutorial dialog is clicked.
*
* @param dialog The dialog that received the click
* @param which The button that was clicked
*/
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
dialog.dismiss();
}
protected void updateShortcutPreferenceData() {
if (mComponentName == null) {
return;
}
final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(
getPrefContext(), mComponentName);
if (shortcutTypes != DEFAULT) {
final PreferredShortcut shortcut = new PreferredShortcut(
mComponentName.flattenToString(), shortcutTypes);
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
}
}
protected void updateShortcutPreference() {
if (mComponentName == null) {
return;
}
final int shortcutTypes = getUserPreferredShortcutTypes();
mShortcutPreference.setChecked(
ShortcutUtils.isShortcutContained(
getPrefContext(), shortcutTypes, mComponentName.flattenToString()));
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
protected String getShortcutPreferenceKey() {
return KEY_SHORTCUT_PREFERENCE;
}
@Override
public void onToggleClicked(ShortcutPreference preference) {
if (mComponentName == null) {
return;
}
final int shortcutTypes = getUserPreferredShortcutTypes();
final boolean isChecked = preference.isChecked();
getPrefContext().getSystemService(AccessibilityManager.class).enableShortcutsForTargets(
isChecked, shortcutTypes,
Set.of(mComponentName.flattenToString()), getPrefContext().getUserId());
if (isChecked) {
showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
}
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
@Override
public void onSettingsClicked(ShortcutPreference preference) {
EditShortcutsPreferenceFragment.showEditShortcutScreen(
requireContext(), getMetricsCategory(), getShortcutTitle(),
mComponentName, getIntent());
}
/**
* Setups {@link com.android.internal.R.string#config_defaultAccessibilityService} into
* {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE} if that settings key has never
* been set and only write the key when user enter into corresponding page.
*/
@VisibleForTesting
void writeConfigDefaultAccessibilityServiceIntoShortcutTargetServiceIfNeeded(Context context) {
if (mComponentName == null) {
return;
}
// It might be shortened form (with a leading '.'). Need to unflatten back to ComponentName
// first, or it will encounter errors when getting service from
// `ACCESSIBILITY_SHORTCUT_TARGET_SERVICE`.
final ComponentName configDefaultService = ComponentName.unflattenFromString(
getString(com.android.internal.R.string.config_defaultAccessibilityService));
if (!mComponentName.equals(configDefaultService)) {
return;
}
final String targetKey = Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
final String targetString = Settings.Secure.getString(context.getContentResolver(),
targetKey);
// By intentional, we only need to write the config string when the Settings key has never
// been set (== null). Empty string also means someone already wrote it before, so we need
// to respect the value.
if (targetString == null) {
Settings.Secure.putString(context.getContentResolver(), targetKey,
configDefaultService.flattenToString());
}
}
/** Returns user visible name of the tile by given {@link ComponentName}. */
protected CharSequence loadTileLabel(Context context, ComponentName componentName) {
final PackageManager packageManager = context.getPackageManager();
final Intent queryIntent = new Intent(TileService.ACTION_QS_TILE);
final List<ResolveInfo> resolveInfos =
packageManager.queryIntentServices(queryIntent, PackageManager.GET_META_DATA);
for (ResolveInfo info : resolveInfos) {
final ServiceInfo serviceInfo = info.serviceInfo;
if (TextUtils.equals(componentName.getPackageName(), serviceInfo.packageName)
&& TextUtils.equals(componentName.getClassName(), serviceInfo.name)) {
return serviceInfo.loadLabel(packageManager);
}
}
return null;
}
@VisibleForTesting
boolean isAnySetupWizard() {
return WizardManagerHelper.isAnySetupWizard(getIntent());
}
/**
* Returns the default preferred shortcut types when the user doesn't have a preferred shortcut
* types
*/
@ShortcutConstants.UserShortcutType
protected int getDefaultShortcutTypes() {
return SOFTWARE;
}
/**
* Returns the user preferred shortcut types or the default shortcut types if not set
*/
@ShortcutConstants.UserShortcutType
protected int getUserPreferredShortcutTypes() {
return PreferredShortcuts.retrieveUserShortcutType(
getPrefContext(), mComponentName.flattenToString(), getDefaultShortcutTypes());
}
@Override
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
RecyclerView recyclerView =
super.onCreateRecyclerView(inflater, parent, savedInstanceState);
return AccessibilityFragmentUtils.addCollectionInfoToAccessibilityDelegate(recyclerView);
}
}