/* * 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 // , 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 shortcutFeatureKeys = getShortcutFeatureSettingsKeys(); contentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> { updateShortcutPreferenceData(); updateShortcutPreference(); }); } protected List getShortcutFeatureSettingsKeys() { final List 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. * *

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 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 getPreferenceOrderList() { final List 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 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); } }