Merge 25Q1 (ab/12770256) to aosp-main-future

Bug: 385190204
Merged-In: Iaee6792d1a27be8fa4b443f783a47a3715b6d3a1
Change-Id: I0ac29cecfec526a38cf4a120b8ef704ee7bc01b3
This commit is contained in:
Xin Li
2025-02-26 11:59:26 -08:00
1291 changed files with 58828 additions and 22728 deletions

View File

@@ -147,9 +147,24 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener {
* @return any subscription within device is under ECM mode
*/
public boolean isInEcmMode() {
return isInEcmMode(mContext, mTelephonyManager);
}
/**
* Check the status of ECM mode
*
* @param context Caller's {@link Context}
* @param telephonyManager The default {@link TelephonyManager}
*
* @return any subscription within device is under ECM mode
*/
public static boolean isInEcmMode(Context context, TelephonyManager telephonyManager) {
if (context == null || telephonyManager == null) {
return false;
}
if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
try {
if (mTelephonyManager.getEmergencyCallbackMode()) {
if (telephonyManager.getEmergencyCallbackMode()) {
return true;
}
} catch (UnsupportedOperationException e) {
@@ -157,26 +172,26 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener {
// Ignore exception, device is not in ECM mode.
}
} else {
if (mTelephonyManager.getEmergencyCallbackMode()) {
if (telephonyManager.getEmergencyCallbackMode()) {
return true;
}
}
final List<SubscriptionInfo> subInfoList =
ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionsInfo();
ProxySubscriptionManager.getInstance(context).getActiveSubscriptionsInfo();
if (subInfoList == null) {
return false;
}
for (SubscriptionInfo subInfo : subInfoList) {
final TelephonyManager telephonyManager =
mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
if (telephonyManager != null) {
final TelephonyManager telephonyManagerForSubId =
telephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
if (telephonyManagerForSubId != null) {
if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) {
if (telephonyManager.getEmergencyCallbackMode()) {
if (telephonyManagerForSubId.getEmergencyCallbackMode()) {
return true;
}
} else {
try {
if (telephonyManager.getEmergencyCallbackMode()) {
if (telephonyManagerForSubId.getEmergencyCallbackMode()) {
return true;
}
} catch (UnsupportedOperationException e) {

View File

@@ -20,9 +20,13 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.display.BrightnessLevelPreferenceController;
import com.android.settings.display.CameraGesturePreferenceController;
import com.android.settings.display.DisplayScreen;
import com.android.settings.display.LiftToWakePreferenceController;
import com.android.settings.display.ShowOperatorNamePreferenceController;
import com.android.settings.display.TapToWakePreferenceController;
@@ -92,4 +96,9 @@ public class DisplaySettings extends DashboardFragment {
return buildPreferenceControllers(context, null);
}
};
@Override
public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
return DisplayScreen.KEY;
}
}

View File

@@ -17,8 +17,13 @@
package com.android.settings;
import android.app.settings.SettingsEnums;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.deviceinfo.legal.LegalSettingsScreen;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
@@ -44,4 +49,9 @@ public class LegalSettings extends DashboardFragment {
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.about_legal);
@Override
public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
return LegalSettingsScreen.KEY;
}
}

View File

@@ -17,6 +17,8 @@
package com.android.settings;
import static android.content.Context.MODE_PRIVATE;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.ProgressDialog;
@@ -25,6 +27,7 @@ import android.app.admin.FactoryResetProtectionPolicy;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -33,6 +36,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.service.oemlock.OemLockManager;
import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -42,6 +46,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.google.android.setupcompat.template.FooterBarMixin;
@@ -90,7 +95,7 @@ public class MainClearConfirm extends InstrumentedFragment {
} else {
pdbManager = null;
}
setSimDialogProgressState();
if (shouldWipePersistentDataBlock(pdbManager)) {
new AsyncTask<Void, Void, Void>() {
@@ -128,6 +133,17 @@ public class MainClearConfirm extends InstrumentedFragment {
} else {
doMainClear();
}
}
private void setSimDialogProgressState() {
if (getActivity() != null) {
final SharedPreferences prefs = getActivity().getSharedPreferences(
SubscriptionActionDialogActivity.SIM_ACTION_DIALOG_PREFS, MODE_PRIVATE);
prefs.edit().putInt(SubscriptionActionDialogActivity.KEY_PROGRESS_STATE,
SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING).apply();
Log.d(TAG, "SIM dialog setProgressState: 1");
}
}
private ProgressDialog getProgressDialog() {

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 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
import android.content.Context
import android.os.UserHandle
import android.os.UserManager
import androidx.annotation.CallSuper
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.metadata.PreferenceRestrictionProvider
/** Mixin to support restriction. */
interface PreferenceRestrictionMixin : PreferenceRestrictionProvider {
/**
* Keys for restriction.
*
* Preference is restricted when **ANY** key in the list is restricted.
*/
val restrictionKeys: Array<String>
val useAdminDisabledSummary: Boolean
get() = false
@CallSuper fun isEnabled(context: Context) = !context.hasBaseUserRestriction(restrictionKeys)
override fun isRestricted(context: Context) =
context.getRestrictionEnforcedAdmin(restrictionKeys) != null
}
/** Returns the admin that has enforced restriction on given keys. */
fun Context.getRestrictionEnforcedAdmin(restrictionKeys: Array<String>): EnforcedAdmin? {
val userId = UserHandle.myUserId()
return restrictionKeys.firstNotNullOfOrNull {
RestrictedLockUtilsInternal.checkIfRestrictionEnforced(this, it, userId)
}
}
/** Returns if there is **any** base user restriction on given keys. */
fun Context.hasBaseUserRestriction(restrictionKeys: Array<String>): Boolean {
val userManager = getSystemService(UserManager::class.java) ?: return false
val userHandle = UserHandle.of(UserHandle.myUserId())
return restrictionKeys.any { userManager.hasBaseUserRestriction(it, userHandle) }
}

View File

@@ -69,6 +69,6 @@ class RegulatoryInfoDisplayActivity : Activity() {
private fun getRegulatoryText(): CharSequence? {
val regulatoryInfoText = resources.getText(R.string.regulatory_info_text)
if (regulatoryInfoText.isNotBlank()) return regulatoryInfoText
return featureFactory.hardwareInfoFeatureProvider?.countryIfOriginLabel
return featureFactory.hardwareInfoFeatureProvider?.countryOfOriginLabel
}
}

View File

@@ -47,7 +47,6 @@ import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.network.ResetNetworkRestrictionViewBuilder;
@@ -142,13 +141,10 @@ public class ResetNetwork extends InstrumentedFragment {
Context context = getContext();
boolean resetSims = false;
// TODO(b/317276437) Simplify the logic once flag is released
int resetOptions = ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER
| ResetNetworkRequest.RESET_VPN_MANAGER;
if (Flags.resetMobileNetworkSettings()) {
resetOptions |= ResetNetworkRequest.RESET_IMS_STACK;
resetOptions |= ResetNetworkRequest.RESET_PHONE_PROCESS;
}
| ResetNetworkRequest.RESET_VPN_MANAGER
| ResetNetworkRequest.RESET_IMS_STACK
| ResetNetworkRequest.RESET_PHONE_PROCESS;
ResetNetworkRequest request = new ResetNetworkRequest(resetOptions);
if (mSubscriptions != null && mSubscriptions.size() > 0) {
int selectedIndex = mSubscriptionSpinner.getSelectedItemPosition();
@@ -156,9 +152,7 @@ public class ResetNetwork extends InstrumentedFragment {
int subId = subscription.getSubscriptionId();
request.setResetTelephonyAndNetworkPolicyManager(subId)
.setResetApn(subId);
if (Flags.resetMobileNetworkSettings()) {
request.setResetImsSubId(subId);
}
request.setResetImsSubId(subId);
}
if (mEsimContainer.getVisibility() == View.VISIBLE && mEsimCheckbox.isChecked()) {
resetSims = true;

View File

@@ -33,6 +33,7 @@ import android.widget.CheckedTextView;
import android.widget.ListAdapter;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AlertDialog.Builder;
import androidx.preference.ListPreferenceDialogFragmentCompat;
@@ -40,11 +41,14 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreferenceHelper;
import com.android.settingslib.RestrictedPreferenceHelperProvider;
import java.util.ArrayList;
import java.util.List;
public class RestrictedListPreference extends CustomListPreference {
public class RestrictedListPreference extends CustomListPreference implements
RestrictedPreferenceHelperProvider {
private final RestrictedPreferenceHelper mHelper;
private final List<RestrictedItem> mRestrictedItems = new ArrayList<>();
private boolean mRequiresActiveUnlockedProfile = false;
@@ -61,6 +65,11 @@ public class RestrictedListPreference extends CustomListPreference {
mHelper = new RestrictedPreferenceHelper(context, this, attrs);
}
@Override
public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
return mHelper;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);

View File

@@ -92,13 +92,17 @@ public class Settings extends SettingsActivity {
public static class AvailableVirtualKeyboardActivity extends SettingsActivity { /* empty */ }
public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ }
public static class PhysicalKeyboardActivity extends SettingsActivity { /* empty */ }
public static class PhysicalKeyboardLayoutPickerActivity extends SettingsActivity {
/* empty */
}
public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ }
public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ }
public static class LocalePickerActivity extends SettingsActivity { /* empty */ }
public static class LanguageAndInputSettingsActivity extends SettingsActivity { /* empty */ }
public static class LanguageSettingsActivity extends SettingsActivity { /* empty */ }
/** Activity for the regional preferences settings. */
public static class RegionalPreferencesActivity extends SettingsActivity { /* empty */ }
public static class TemperatureUnitSettingsActivity extends SettingsActivity { /* empty */ }
public static class FirstDayOfWeekSettingsActivity extends SettingsActivity { /* empty */ }
public static class KeyboardSettingsActivity extends SettingsActivity { /* empty */ }
/** Activity for the navigation mode settings. */
public static class NavigationModeSettingsActivity extends SettingsActivity { /* empty */ }
@@ -112,6 +116,7 @@ public class Settings extends SettingsActivity {
public static class ModuleLicensesActivity extends SettingsActivity { /* empty */ }
public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ }
public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ }
public static class AppStorageSettingsActivity extends SettingsActivity { /* empty */ }
public static class ManageAssistActivity extends SettingsActivity { /* empty */ }
public static class HighPowerApplicationsActivity extends SettingsActivity { /* empty */ }
public static class BackgroundCheckSummaryActivity extends SettingsActivity { /* empty */ }
@@ -485,6 +490,7 @@ public class Settings extends SettingsActivity {
public static class NetworkDashboardActivity extends SettingsActivity {}
public static class ConnectedDeviceDashboardActivity extends SettingsActivity {}
public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ }
public static class PowerUsageAdvancedActivity extends SettingsActivity { /* empty */ }
public static class StorageDashboardActivity extends SettingsActivity {}
public static class AccountDashboardActivity extends SettingsActivity {}
public static class SystemDashboardActivity extends SettingsActivity {}
@@ -515,4 +521,5 @@ public class Settings extends SettingsActivity {
public static class HearingDevicesActivity extends SettingsActivity { /* empty */ }
public static class HearingDevicesPairingActivity extends SettingsActivity { /* empty */ }
public static class ContactsStorageSettingsActivity extends SettingsActivity { /* empty */ }
}

View File

@@ -73,6 +73,7 @@ import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.core.instrumentation.SharedPreferencesLogger;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.widget.SettingsThemeHelper;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -169,6 +170,9 @@ public class SettingsActivity extends SettingsBaseActivity
private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
private static final int EXPRESSIVE_BACK_ICON =
com.android.settingslib.collapsingtoolbar.R.drawable.settingslib_expressive_icon_back;
private String mFragmentClass;
private String mHighlightMenuKey;
@@ -301,10 +305,17 @@ public class SettingsActivity extends SettingsBaseActivity
// If this is in setup flow, don't apply theme. Because light theme needs to be applied
// in SettingsBaseActivity#onCreate().
if (isSubSettings(intent) && !WizardManagerHelper.isAnySetupWizard(getIntent())) {
setTheme(R.style.Theme_SubSettings);
int themeId = SettingsThemeHelper.isExpressiveTheme(this)
? R.style.Theme_SubSettings_Expressive : R.style.Theme_SubSettings;
setTheme(themeId);
}
setContentView(R.layout.settings_main_prefs);
mMainSwitch = findViewById(R.id.switch_bar);
if (mMainSwitch != null) {
mMainSwitch.setMetricsCategory(lookupMetricsCategory());
mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1);
}
getSupportFragmentManager().addOnBackStackChangedListener(this);
@@ -324,12 +335,6 @@ public class SettingsActivity extends SettingsBaseActivity
launchSettingFragment(initialFragmentName, intent);
}
mMainSwitch = findViewById(R.id.switch_bar);
if (mMainSwitch != null) {
mMainSwitch.setMetricsCategory(lookupMetricsCategory());
mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1);
}
// see if we should show Back/Next buttons
if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
@@ -388,6 +393,9 @@ public class SettingsActivity extends SettingsBaseActivity
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(isActionBarButtonEnabled);
actionBar.setHomeButtonEnabled(isActionBarButtonEnabled);
if (SettingsThemeHelper.isExpressiveTheme(this)) {
actionBar.setHomeAsUpIndicator(EXPRESSIVE_BACK_ICON);
}
actionBar.setDisplayShowTitleEnabled(true);
}
}

View File

@@ -24,6 +24,7 @@ import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri;
import android.provider.Settings;
import android.util.FeatureFlagUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -45,6 +46,7 @@ import com.android.settingslib.datastore.BackupRestoreStorageManager;
import com.android.settingslib.metadata.PreferenceScreenMetadata;
import com.android.settingslib.metadata.PreferenceScreenRegistry;
import com.android.settingslib.metadata.ProvidePreferenceScreenOptions;
import com.android.settingslib.preference.PreferenceBindingFactory;
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -58,8 +60,9 @@ import java.util.List;
)
public class SettingsApplication extends Application {
private static final String TAG = "SettingsApplication";
private WeakReference<SettingsHomepageActivity> mHomeActivity = new WeakReference<>(null);
@Nullable private BiometricsEnvironment mBiometricsEnvironment;
@Nullable volatile private BiometricsEnvironment mBiometricsEnvironment;
@Override
protected void attachBaseContext(Context base) {
@@ -74,6 +77,7 @@ public class SettingsApplication extends Application {
if (Flags.catalyst()) {
PreferenceScreenRegistry.INSTANCE.setPreferenceScreensSupplier(
this::getPreferenceScreens);
PreferenceBindingFactory.setDefaultFactory(new SettingsPreferenceBindingFactory());
}
BackupRestoreStorageManager.getInstance(this)
@@ -138,20 +142,23 @@ public class SettingsApplication extends Application {
@Nullable
public BiometricsEnvironment getBiometricEnvironment() {
if (Flags.fingerprintV2Enrollment()) {
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
final FingerprintManager fpm = getSystemService(FingerprintManager.class);
if (mBiometricsEnvironment == null) {
mBiometricsEnvironment = new BiometricsEnvironment(this, fpm);
BiometricsEnvironment localEnvironment = mBiometricsEnvironment;
if (localEnvironment == null) {
synchronized (this) {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
return null;
}
final FingerprintManager fpm = getSystemService(FingerprintManager.class);
localEnvironment = mBiometricsEnvironment;
if (fpm != null && localEnvironment == null) {
mBiometricsEnvironment = localEnvironment = new BiometricsEnvironment(this,
fpm);
} else {
Log.e(TAG, "Error when creating environment, fingerprint manager was null");
}
return mBiometricsEnvironment;
} else {
return null;
}
}
return null;
return localEnvironment;
}
@Override

View File

@@ -39,6 +39,7 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.core.instrumentation.ElapsedTimeUtils;
import com.android.settings.homepage.DeepLinkHomepageActivity;
import com.android.settings.search.SearchStateReceiver;
import com.android.settingslib.utils.ThreadUtils;
@@ -69,6 +70,7 @@ public class SettingsInitialize extends BroadcastReceiver {
webviewSettingSetup(context, pm, userInfo);
ThreadUtils.postOnBackgroundThread(() -> refreshExistingShortcuts(context));
enableTwoPaneDeepLinkActivityIfNecessary(pm, context);
storeSuwCompleteTimestamp(context, broadcast);
}
private void managedProfileSetup(Context context, final PackageManager pm, Intent broadcast,
@@ -161,4 +163,10 @@ public class SettingsInitialize extends BroadcastReceiver {
pm.setComponentEnabledSetting(searchStateReceiver, enableState,
PackageManager.DONT_KILL_APP);
}
private void storeSuwCompleteTimestamp(Context context, Intent broadcast) {
if (SetupWizardUtils.ACTION_SETUP_WIZARD_FINISHED.equals(broadcast.getAction())) {
ElapsedTimeUtils.storeSuwFinishedTimestamp(context, System.currentTimeMillis());
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2024 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
import androidx.preference.Preference
import com.android.settingslib.RestrictedPreferenceHelperProvider
import com.android.settingslib.metadata.PreferenceHierarchyNode
import com.android.settingslib.preference.DefaultPreferenceBindingFactory
import com.android.settingslib.preference.PreferenceBinding
/** Preference binding factory for settings app. */
class SettingsPreferenceBindingFactory : DefaultPreferenceBindingFactory() {
override fun bind(
preference: Preference,
node: PreferenceHierarchyNode,
preferenceBinding: PreferenceBinding?,
) {
super.bind(preference, node, preferenceBinding)
// handle restriction consistently
val metadata = node.metadata
if (metadata is PreferenceRestrictionMixin) {
if (preference is RestrictedPreferenceHelperProvider) {
preference.getRestrictedPreferenceHelper().apply {
useAdminDisabledSummary(metadata.useAdminDisabledSummary)
val context = preference.context
val restrictionKeys = metadata.restrictionKeys
if (!context.hasBaseUserRestriction(restrictionKeys)) {
setDisabledByAdmin(context.getRestrictionEnforcedAdmin(restrictionKeys))
}
}
}
}
}
}

View File

@@ -16,6 +16,9 @@
package com.android.settings;
import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
import static com.android.settingslib.media.PhoneMediaDevice.isDesktop;
import android.app.Activity;
import android.app.Dialog;
import android.app.admin.DevicePolicyManager;
@@ -33,6 +36,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.XmlRes;
import androidx.fragment.app.DialogFragment;
@@ -45,12 +50,14 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.flags.Flags;
import com.android.settings.support.actionbar.HelpResourceProvider;
import com.android.settings.widget.HighlightablePreferenceGroupAdapter;
import com.android.settings.widget.LoadingViewController;
import com.android.settingslib.CustomDialogPreferenceCompat;
import com.android.settingslib.CustomEditTextPreferenceCompat;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.preference.PreferenceScreenCreator;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.widget.LayoutPreference;
@@ -173,6 +180,31 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
}
}
@Override
protected final int getPreferenceScreenResId(@NonNull Context context) {
return getPreferenceScreenResId();
}
/** Returns if catalyst is enabled on current screen. */
public final boolean isCatalystEnabled() {
// TODO(b/379130874): make Catalyst compatible with desktop device, such as user restriction
// check.
Context context = getContext();
if (context != null && isDesktop(context)) {
return false;
}
return getPreferenceScreenCreator() != null;
}
protected @Nullable PreferenceScreenCreator getPreferenceScreenCreator() {
if (!Flags.catalyst()) {
return null;
}
Context context = getContext();
return context != null ? getPreferenceScreenCreator(context) : null;
}
public View setPinnedHeaderView(int layoutResId) {
final LayoutInflater inflater = getActivity().getLayoutInflater();
final View pinnedHeader =
@@ -367,9 +399,13 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
@Override
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
final Bundle arguments = getArguments();
mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen,
arguments == null
? null : arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY),
String key = arguments == null ? null : arguments.getString(EXTRA_FRAGMENT_ARG_KEY);
if (Flags.catalyst() && key == null) {
Activity activity = getActivity();
Intent intent = activity != null ? activity.getIntent() : null;
key = intent != null ? intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY) : null;
}
mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen, key,
mPreferenceHighlighted);
return mAdapter;
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 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
import android.app.Application
import android.content.Intent
import com.android.settings.flags.Flags
import com.android.settingslib.graph.PreferenceGetterRequest
import com.android.settingslib.graph.PreferenceSetterRequest
import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.service.PreferenceService
/** Service to expose settings APIs. */
class SettingsService :
PreferenceService(
graphPermissionChecker = ApiPermissionChecker.alwaysAllow(),
setterPermissionChecker = SetterPermissionChecker(),
getterPermissionChecker = GetterPermissionChecker(),
) {
override fun onBind(intent: Intent) =
if (Flags.catalystService()) super.onBind(intent) else null
}
/** Permission checker for external setter API. */
private class SetterPermissionChecker : ApiPermissionChecker<PreferenceSetterRequest> {
override fun hasPermission(
application: Application,
myUid: Int,
callingUid: Int,
request: PreferenceSetterRequest,
) = true
}
/** Permission checker for external getter API. */
private class GetterPermissionChecker : ApiPermissionChecker<PreferenceGetterRequest> {
override fun hasPermission(
application: Application,
myUid: Int,
callingUid: Int,
request: PreferenceGetterRequest,
) = true
}

View File

@@ -32,6 +32,9 @@ import java.util.Arrays;
public class SetupWizardUtils {
public static final String ACTION_SETUP_WIZARD_FINISHED =
"com.google.android.setupwizard.SETUP_WIZARD_FINISHED";
public static String getThemeString(Intent intent) {
String theme = intent.getStringExtra(WizardManagerHelper.EXTRA_THEME);
if (theme == null) {

View File

@@ -291,23 +291,23 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment
// Certificates can be installed into SYSTEM_UID or WIFI_UID through CertInstaller.
final int myUserId = UserHandle.myUserId();
final int systemUid = UserHandle.getUid(myUserId, Process.SYSTEM_UID);
final int wifiUid = UserHandle.getUid(myUserId, Process.WIFI_UID);
try {
KeyStore processKeystore = KeyStore.getInstance(KEYSTORE_PROVIDER);
processKeystore.load(null);
KeyStore wifiKeystore = null;
if (myUserId == 0) {
wifiKeystore = KeyStore.getInstance(KEYSTORE_PROVIDER);
wifiKeystore.load(new AndroidKeyStoreLoadStoreParameter(
KeyProperties.NAMESPACE_WIFI));
}
List<Credential> credentials = new ArrayList<>();
credentials.addAll(getCredentialsForUid(processKeystore, systemUid).values());
if (wifiKeystore != null) {
credentials.addAll(getCredentialsForUid(wifiKeystore, wifiUid).values());
UserManager userManager = getContext().getSystemService(UserManager.class);
if (userManager.isAdminUser()) {
wifiKeystore = KeyStore.getInstance(KEYSTORE_PROVIDER);
wifiKeystore.load(
new AndroidKeyStoreLoadStoreParameter(KeyProperties.NAMESPACE_WIFI));
credentials.addAll(
getCredentialsForUid(wifiKeystore, Process.WIFI_UID).values());
}
return credentials;
} catch (Exception e) {
throw new RuntimeException("Failed to load credentials from Keystore.", e);

View File

@@ -131,6 +131,7 @@ import com.android.settings.password.ConfirmDeviceCredentialActivity;
import com.android.settingslib.widget.ActionBarShadowController;
import com.android.settingslib.widget.AdaptiveIcon;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@@ -1180,9 +1181,9 @@ public final class Utils extends com.android.settingslib.Utils {
try {
return context.getPackageManager().getApplicationInfo(packageName, 0).enabled;
} catch (Exception e) {
Log.e(TAG, "Error while retrieving application info for package " + packageName, e);
// Expected, package is not installed or not enabled.
return false;
}
return false;
}
/** Get {@link Resources} by subscription id if subscription id is valid. */
@@ -1329,8 +1330,7 @@ public final class Utils extends com.android.settingslib.Utils {
*/
@ColorInt
public static int getHomepageIconColor(Context context) {
return getColorAttrDefaultColor(
context, com.android.internal.R.attr.materialColorOnSurface);
return context.getColor(com.android.internal.R.color.materialColorOnSurface);
}
/**
@@ -1516,13 +1516,13 @@ public final class Utils extends com.android.settingslib.Utils {
final UserManager userManager = context.getSystemService(
UserManager.class);
final int status = biometricManager.canAuthenticate(getEffectiveUserId(
userManager, userId), BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
userManager, userId), BiometricManager.Authenticators.IDENTITY_CHECK);
switch(status) {
case BiometricManager.BIOMETRIC_SUCCESS:
return BiometricStatus.OK;
case BiometricManager.BIOMETRIC_ERROR_LOCKOUT:
return BiometricStatus.LOCKOUT;
case BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE:
case BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE:
case BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS:
return BiometricStatus.NOT_ACTIVE;
default:
@@ -1582,7 +1582,7 @@ public final class Utils extends com.android.settingslib.Utils {
final Intent intent = new Intent();
if (android.hardware.biometrics.Flags.mandatoryBiometrics()) {
intent.putExtra(BIOMETRIC_PROMPT_AUTHENTICATORS,
BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
BiometricManager.Authenticators.IDENTITY_CHECK);
}
intent.putExtra(BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT,
resources.getString(R.string.cancel));
@@ -1600,4 +1600,19 @@ public final class Utils extends com.android.settingslib.Utils {
pm.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
/**
* Returns {@code true} if the supplied package is a protected package. Otherwise, returns
* {@code false}.
*
* @param context the context
* @param packageName the package name
*/
public static boolean isProtectedPackage(
@NonNull Context context, @NonNull String packageName) {
final List<String> protectedPackageNames = Arrays.asList(context.getResources()
.getStringArray(com.android.internal.R.array
.config_biometric_protected_package_names));
return protectedPackageNames != null && protectedPackageNames.contains(packageName);
}
}

View File

@@ -19,12 +19,18 @@ package com.android.settings.accessibility;
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.util.FeatureFlagUtils;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
/** Settings fragment containing bluetooth audio routing. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class AccessibilityAudioRoutingFragment extends RestrictedDashboardFragment {
private static final String TAG = "AccessibilityAudioRoutingFragment";
@@ -47,6 +53,25 @@ public class AccessibilityAudioRoutingFragment extends RestrictedDashboardFragme
return TAG;
}
@VisibleForTesting
static boolean isPageSearchEnabled(Context context) {
if (!FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) {
return false;
}
final HearingAidHelper mHelper = new HearingAidHelper(context);
return mHelper.isHearingAidSupported();
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_audio_routing_fragment);
new BaseSearchIndexProvider(R.xml.accessibility_audio_routing_fragment) {
@Override
protected boolean isPageSearchEnabled(Context context) {
if (Flags.fixA11ySettingsSearch()) {
return AccessibilityAudioRoutingFragment.isPageSearchEnabled(context);
} else {
return super.isPageSearchEnabled(context);
}
}
};
}

View File

@@ -49,9 +49,14 @@ public class AccessibilityButtonFooterPreferenceController extends
public void displayPreference(PreferenceScreen screen) {
// Need to update footerPreference's data before super.displayPreference(), then it will use
// data to update related property of footerPreference.
final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_description
: R.string.accessibility_button_description;
final int titleResource;
if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
titleResource = R.string.accessibility_button_description;
} else {
titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_description
: R.string.accessibility_button_description;
}
final CharSequence footerText = Html.fromHtml(
MessageFormat.format(mContext.getString(titleResource), 1, 2, 3),
Html.FROM_HTML_MODE_COMPACT);

View File

@@ -61,7 +61,12 @@ public class AccessibilityButtonPreferenceController extends BasePreferenceContr
}
private int getPreferenceTitleResource() {
return AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_title : R.string.accessibility_button_title;
if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
return R.string.accessibility_button_title;
} else {
return AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_title
: R.string.accessibility_button_title;
}
}
}

View File

@@ -26,6 +26,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceCategory;
import com.android.internal.accessibility.AccessibilityShortcutController;
@@ -118,6 +119,21 @@ public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPrefe
return getText(R.string.accessibility_hearing_device_shortcut_title);
}
@VisibleForTesting
static boolean isPageSearchEnabled(Context context) {
final HearingAidHelper mHelper = new HearingAidHelper(context);
return mHelper.isHearingAidSupported();
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_hearing_aids);
new BaseSearchIndexProvider(R.xml.accessibility_hearing_aids) {
@Override
protected boolean isPageSearchEnabled(Context context) {
if (Flags.fixA11ySettingsSearch()) {
return AccessibilityHearingAidsFragment.isPageSearchEnabled(context);
} else {
return super.isPageSearchEnabled(context);
}
}
};
}

View File

@@ -1,149 +0,0 @@
/*
* Copyright (C) 2022 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.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
/** PrimarySwitchPreferenceController that shows quick settings tooltip on first use. */
public abstract class AccessibilityQuickSettingsPrimarySwitchPreferenceController
extends TogglePreferenceController
implements LifecycleObserver, OnCreate, OnDestroy, OnSaveInstanceState {
private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
private final Handler mHandler;
private PrimarySwitchPreference mPreference;
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
private boolean mNeedsQSTooltipReshow = false;
/** Returns the accessibility tile component name. */
@Nullable
abstract ComponentName getTileComponentName();
/** Returns the accessibility tile tooltip content. */
abstract CharSequence getTileTooltipContent();
public AccessibilityQuickSettingsPrimarySwitchPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey);
mHandler = new Handler(context.getMainLooper());
}
@Override
public void onCreate(Bundle savedInstanceState) {
// Restore the tooltip.
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
}
}
}
@Override
public void onDestroy() {
mHandler.removeCallbacksAndMessages(null);
final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
if (isTooltipWindowShowing) {
mTooltipWindow.dismiss();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
}
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
if (mNeedsQSTooltipReshow) {
mHandler.post(this::showQuickSettingsTooltipIfNeeded);
}
}
@Override
public boolean setChecked(boolean isChecked) {
if (isChecked) {
showQuickSettingsTooltipIfNeeded();
}
return isChecked;
}
@Override
public boolean isChecked() {
return false;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
private void showQuickSettingsTooltipIfNeeded() {
if (mPreference == null) {
// Returns if no preference found by slice highlight menu.
return;
}
final ComponentName tileComponentName = getTileComponentName();
if (tileComponentName == null) {
// Returns if no tile service assigned.
return;
}
if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
mContext, tileComponentName)) {
// Returns if quick settings tooltip only show once.
return;
}
// TODO (287728819): Move tooltip showing to SystemUI
// Since the lifecycle of controller is independent of that of the preference, doing
// null check on switch is a temporary solution for the case that switch view
// is not ready when we would like to show the tooltip. If the switch is not ready,
// we give up showing the tooltip and also do not reshow it in the future.
if (mPreference.getSwitch() != null) {
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
mTooltipWindow.setup(getTileTooltipContent(),
R.drawable.accessibility_auto_added_qs_tooltip_illustration);
mTooltipWindow.showAtTopCenter(mPreference.getSwitch());
}
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext, tileComponentName);
mNeedsQSTooltipReshow = false;
}
}

View File

@@ -30,7 +30,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.view.InputDevice;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
@@ -44,7 +43,6 @@ import com.android.internal.content.PackageMonitor;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.RestrictedPreference;
@@ -72,14 +70,12 @@ public class AccessibilitySettings extends DashboardFragment implements
private static final String CATEGORY_DISPLAY = "display_category";
@VisibleForTesting
static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
private static final String CATEGORY_KEYBOARD_OPTIONS = "physical_keyboard_options_category";
@VisibleForTesting
static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
private static final String[] CATEGORIES = new String[]{
CATEGORY_SCREEN_READER, CATEGORY_CAPTIONS, CATEGORY_AUDIO, CATEGORY_DISPLAY,
CATEGORY_SPEECH, CATEGORY_INTERACTION_CONTROL,
CATEGORY_KEYBOARD_OPTIONS, CATEGORY_DOWNLOADED_SERVICES
CATEGORY_SPEECH, CATEGORY_INTERACTION_CONTROL, CATEGORY_DOWNLOADED_SERVICES
};
// Extras passed to sub-fragments.
@@ -181,9 +177,7 @@ public class AccessibilitySettings extends DashboardFragment implements
// Observe changes from accessibility selection menu
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
if (android.view.accessibility.Flags.a11yQsShortcut()) {
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
}
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_STICKY_KEYS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SLOW_KEYS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS);
@@ -272,7 +266,7 @@ public class AccessibilitySettings extends DashboardFragment implements
* @return The service summary
*/
public static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info,
boolean serviceEnabled) {
boolean serviceEnabled) {
if (serviceEnabled && info.crashed) {
return context.getText(R.string.accessibility_summary_state_stopped);
}
@@ -413,10 +407,8 @@ public class AccessibilitySettings extends DashboardFragment implements
final List<RestrictedPreference> preferenceList = getInstalledAccessibilityPreferences(
getPrefContext(), installedShortcutList, installedServiceList);
if (Flags.checkPrebundledIsPreinstalled()) {
removeNonPreinstalledComponents(mPreBundledServiceComponentToCategoryMap,
installedShortcutList, installedServiceList);
}
removeNonPreinstalledComponents(mPreBundledServiceComponentToCategoryMap,
installedShortcutList, installedServiceList);
final PreferenceCategory downloadedServicesCategory =
mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES);
@@ -458,7 +450,6 @@ public class AccessibilitySettings extends DashboardFragment implements
// Hide category if it is empty.
updatePreferenceCategoryVisibility(CATEGORY_SCREEN_READER);
updatePreferenceCategoryVisibility(CATEGORY_SPEECH);
updatePreferenceCategoryVisibility(CATEGORY_KEYBOARD_OPTIONS);
}
/**
@@ -548,9 +539,7 @@ public class AccessibilitySettings extends DashboardFragment implements
/**
* Updates preferences related to system configurations.
*/
protected void updateSystemPreferences() {
updateKeyboardPreferencesVisibility();
}
protected void updateSystemPreferences() {}
private void updatePreferencesState() {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
@@ -559,53 +548,6 @@ public class AccessibilitySettings extends DashboardFragment implements
findPreference(controller.getPreferenceKey())));
}
private void updateKeyboardPreferencesVisibility() {
if (!mCategoryToPrefCategoryMap.containsKey(CATEGORY_KEYBOARD_OPTIONS)) {
return;
}
boolean isVisible = isAnyHardKeyboardsExist()
&& isAnyKeyboardPreferenceAvailable();
mCategoryToPrefCategoryMap.get(CATEGORY_KEYBOARD_OPTIONS).setVisible(
isVisible);
if (isVisible) {
//set summary here.
findPreference(KeyboardBounceKeyPreferenceController.PREF_KEY).setSummary(
getContext().getString(R.string.bounce_keys_summary,
PhysicalKeyboardFragment.BOUNCE_KEYS_THRESHOLD));
findPreference(KeyboardSlowKeyPreferenceController.PREF_KEY).setSummary(
getContext().getString(R.string.slow_keys_summary,
PhysicalKeyboardFragment.SLOW_KEYS_THRESHOLD));
}
}
static boolean isAnyHardKeyboardsExist() {
for (int deviceId : InputDevice.getDeviceIds()) {
final InputDevice device = InputDevice.getDevice(deviceId);
if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
return true;
}
}
return false;
}
private boolean isAnyKeyboardPreferenceAvailable() {
for (List<AbstractPreferenceController> controllerList : getPreferenceControllers()) {
for (AbstractPreferenceController controller : controllerList) {
if (controller.getPreferenceKey().equals(
KeyboardBounceKeyPreferenceController.PREF_KEY)
|| controller.getPreferenceKey().equals(
KeyboardSlowKeyPreferenceController.PREF_KEY)
|| controller.getPreferenceKey().equals(
KeyboardStickyKeyPreferenceController.PREF_KEY)) {
if (controller.isAvailable()) {
return true;
}
}
}
}
return false;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_settings) {
@Override
@@ -663,10 +605,12 @@ public class AccessibilitySettings extends DashboardFragment implements
};
@Override
public void onInputDeviceAdded(int deviceId) {}
public void onInputDeviceAdded(int deviceId) {
}
@Override
public void onInputDeviceRemoved(int deviceId) {}
public void onInputDeviceRemoved(int deviceId) {
}
@Override
public void onInputDeviceChanged(int deviceId) {

View File

@@ -41,8 +41,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.display.AutoBrightnessPreferenceController;
import com.android.settings.display.BrightnessLevelPreferenceController;
import com.android.settings.display.BrightnessLevelPreferenceControllerForSetupWizard;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -168,16 +167,10 @@ public class AccessibilitySettingsForSetupWizard extends DashboardFragment
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
// Requires lifecycle, so added programmatically (normally via resId).
final List<AbstractPreferenceController> controllers = new ArrayList<>();
BrightnessLevelPreferenceController brightnessLevelPreferenceController =
new BrightnessLevelPreferenceController(context, getSettingsLifecycle());
brightnessLevelPreferenceController.setInSetupWizard(true);
controllers.add(brightnessLevelPreferenceController);
String autoBrightnessKey = context.getString(R.string.preference_key_auto_brightness);
AutoBrightnessPreferenceController autoBrightnessPreferenceController =
new AutoBrightnessPreferenceController(context, autoBrightnessKey);
autoBrightnessPreferenceController.setInSetupWizard(true);
controllers.add(autoBrightnessPreferenceController);
controllers.add(new BrightnessLevelPreferenceControllerForSetupWizard(
context, getSettingsLifecycle()));
return controllers;
}

View File

@@ -17,24 +17,21 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
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 static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_QS_TOOLTIP_TYPE;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.icu.text.CaseMap;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -46,18 +43,16 @@ import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settings.utils.LocaleUtils;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
* Base class for accessibility fragments shortcut functions and dialog management.
@@ -120,9 +115,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
if (android.view.accessibility.Flags.a11yQsShortcut()) {
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
}
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> {
updateShortcutPreferenceData();
@@ -241,6 +234,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
);
}
@SuppressLint("MissingPermission")
@Override
public void onToggleClicked(ShortcutPreference preference) {
if (getComponentName() == null) {
@@ -248,13 +242,12 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
}
final int shortcutTypes = getUserPreferredShortcutTypes();
if (preference.isChecked()) {
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes,
getComponentName());
final boolean isChecked = preference.isChecked();
getPrefContext().getSystemService(AccessibilityManager.class).enableShortcutsForTargets(
isChecked, shortcutTypes,
Set.of(getComponentName().flattenToString()), getPrefContext().getUserId());
if (isChecked) {
showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
} else {
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes,
getComponentName());
}
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
@@ -340,37 +333,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
}
final int shortcutTypes = getUserPreferredShortcutTypes();
// LINT.IfChange(shortcut_type_ui_order)
final List<CharSequence> list = new ArrayList<>();
if (android.view.accessibility.Flags.a11yQsShortcut()) {
if (hasShortcutType(shortcutTypes, QUICK_SETTINGS)) {
final CharSequence qsTitle = context.getText(
R.string.accessibility_feature_shortcut_setting_summary_quick_settings);
list.add(qsTitle);
}
}
if (hasShortcutType(shortcutTypes, SOFTWARE)) {
list.add(getSoftwareShortcutTypeSummary(context));
}
if (hasShortcutType(shortcutTypes, HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
// LINT.ThenChange(/res/xml/accessibility_edit_shortcuts.xml:shortcut_type_ui_order)
// Show software shortcut if first time to use.
if (list.isEmpty()) {
list.add(getSoftwareShortcutTypeSummary(context));
}
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
null, LocaleUtils.getConcatenatedString(list));
}
private boolean hasShortcutType(int value, @UserShortcutType int type) {
return (value & type) == type;
return getShortcutSummaryList(context, shortcutTypes);
}
protected void updateShortcutPreferenceData() {
@@ -410,38 +373,13 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
showQuickSettingsTooltipIfNeeded();
}
/**
* @deprecated made obsolete by quick settings rollout.
*
* (TODO 367414968: finish removal.)
*/
@Deprecated
private void showQuickSettingsTooltipIfNeeded() {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
// Don't show Quick Settings tooltip
return;
}
final ComponentName tileComponentName = getTileComponentName();
if (tileComponentName == null) {
// Returns if no tile service assigned.
return;
}
if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
getContext(), tileComponentName)) {
// Returns if quick settings tooltip only show once.
return;
}
final CharSequence content = getTileTooltipContent(mNeedsQSTooltipType);
if (TextUtils.isEmpty(content)) {
// Returns if no content of tile tooltip assigned.
return;
}
final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.drawable.accessibility_qs_tooltip_illustration
: R.drawable.accessibility_auto_added_qs_tooltip_illustration;
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
mTooltipWindow.setup(content, imageResId);
mTooltipWindow.showAtTopCenter(getView());
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(getContext(),
tileComponentName);
mNeedsQSTooltipReshow = false;
}
/**

View File

@@ -16,15 +16,19 @@
package com.android.settings.accessibility;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
import android.annotation.SuppressLint;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
@@ -60,14 +64,14 @@ import androidx.core.widget.TextViewCompat;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.android.server.accessibility.Flags;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.LottieColorUtils;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -330,7 +334,8 @@ public final class AccessibilityShortcutsTutorial {
result -> Log.w(TAG, "Invalid image raw resource id: " + imageRawRes,
result));
lottieView.setAnimation(imageRawRes);
lottieView.setRepeatCount(LottieDrawable.INFINITE);
// Follow the Motion Stoppable requirement by using a finite animation.
lottieView.setRepeatCount(0);
LottieColorUtils.applyDynamicColors(context, lottieView);
lottieView.playAnimation();
@@ -396,95 +401,84 @@ public final class AccessibilityShortcutsTutorial {
return textView;
}
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
final int type = SOFTWARE;
final CharSequence title = getSoftwareTitle(context);
final View image = createSoftwareImage(context);
final CharSequence instruction = getSoftwareInstruction(context);
final ImageView indicatorIcon =
createImageView(context, R.drawable.ic_accessibility_page_indicator);
indicatorIcon.setEnabled(false);
return new TutorialPage(type, title, image, indicatorIcon, instruction);
@SuppressLint("SwitchIntDef")
private static CharSequence getShortcutTitle(
@NonNull Context context, @UserShortcutType int shortcutType, int buttonMode) {
return switch (shortcutType) {
case HARDWARE -> context.getText(R.string.accessibility_tutorial_dialog_title_volume);
case SOFTWARE -> getSoftwareTitle(context, buttonMode);
case GESTURE -> context.getText(R.string.accessibility_tutorial_dialog_title_gesture);
case TRIPLETAP -> context.getText(R.string.accessibility_tutorial_dialog_title_triple);
case TWOFINGER_DOUBLETAP -> context.getString(
R.string.accessibility_tutorial_dialog_title_two_finger_double, 2);
case QUICK_SETTINGS -> context.getText(
R.string.accessibility_tutorial_dialog_title_quick_setting);
default -> "";
};
}
private static TutorialPage createHardwareTutorialPage(@NonNull Context context) {
final int type = HARDWARE;
final CharSequence title =
context.getText(R.string.accessibility_tutorial_dialog_title_volume);
final View image =
createIllustrationView(context, R.drawable.accessibility_shortcut_type_volume_keys);
final ImageView indicatorIcon =
createImageView(context, R.drawable.ic_accessibility_page_indicator);
final CharSequence instruction =
context.getText(R.string.accessibility_tutorial_dialog_message_volume);
indicatorIcon.setEnabled(false);
return new TutorialPage(type, title, image, indicatorIcon, instruction);
}
private static TutorialPage createTripleTapTutorialPage(@NonNull Context context) {
final int type = TRIPLETAP;
final CharSequence title =
context.getText(R.string.accessibility_tutorial_dialog_title_triple);
final View image =
createIllustrationViewWithImageRawResource(context,
R.raw.accessibility_shortcut_type_tripletap);
final CharSequence instruction = context.getString(
R.string.accessibility_tutorial_dialog_tripletap_instruction, 3);
final ImageView indicatorIcon =
createImageView(context, R.drawable.ic_accessibility_page_indicator);
indicatorIcon.setEnabled(false);
return new TutorialPage(type, title, image, indicatorIcon, instruction);
}
private static TutorialPage createTwoFingerTripleTapTutorialPage(@NonNull Context context) {
final int type = TWOFINGER_DOUBLETAP;
final int numFingers = 2;
final CharSequence title = context.getString(
R.string.accessibility_tutorial_dialog_title_two_finger_double, numFingers);
final View image =
createIllustrationViewWithImageRawResource(context,
R.raw.accessibility_shortcut_type_2finger_doubletap);
final CharSequence instruction = context.getString(
R.string.accessibility_tutorial_dialog_twofinger_doubletap_instruction, numFingers);
final ImageView indicatorIcon =
createImageView(context, R.drawable.ic_accessibility_page_indicator);
indicatorIcon.setEnabled(false);
return new TutorialPage(type, title, image, indicatorIcon, instruction);
}
private static TutorialPage createQuickSettingsTutorialPage(
@NonNull Context context, @NonNull CharSequence featureName, boolean inSetupWizard) {
final int type = QUICK_SETTINGS;
final CharSequence title =
context.getText(R.string.accessibility_tutorial_dialog_title_quick_setting);
final View image =
createIllustrationView(context,
@SuppressLint("SwitchIntDef")
private static View getShortcutImage(
@NonNull Context context, @UserShortcutType int shortcutType, int buttonMode) {
return switch (shortcutType) {
case HARDWARE -> createIllustrationView(
context, R.drawable.accessibility_shortcut_type_volume_keys);
case SOFTWARE -> createSoftwareImage(context, buttonMode);
case GESTURE -> createIllustrationView(context,
AccessibilityUtil.isTouchExploreEnabled(context)
? R.drawable.accessibility_shortcut_type_gesture_touch_explore_on
: R.drawable.accessibility_shortcut_type_gesture);
case TRIPLETAP -> createIllustrationViewWithImageRawResource(context,
R.raw.accessibility_shortcut_type_tripletap);
case TWOFINGER_DOUBLETAP -> createIllustrationViewWithImageRawResource(context,
R.raw.accessibility_shortcut_type_2finger_doubletap);
case QUICK_SETTINGS -> {
View v = createIllustrationView(context,
R.drawable.accessibility_shortcut_type_quick_settings);
// Remove the unneeded background, since the main image already includes a background
image.findViewById(R.id.image_background).setVisibility(GONE);
final int numFingers = AccessibilityUtil.isTouchExploreEnabled(context) ? 2 : 1;
Map<String, Object> arguments = new ArrayMap<>();
arguments.put("count", numFingers);
arguments.put("featureName", featureName);
final CharSequence instruction = StringUtil.getIcuPluralsString(context,
arguments,
R.string.accessibility_tutorial_dialog_message_quick_setting);
final SpannableStringBuilder tutorialText = new SpannableStringBuilder();
if (inSetupWizard) {
tutorialText.append(context.getText(
R.string.accessibility_tutorial_dialog_shortcut_unavailable_in_suw))
.append("\n\n");
}
tutorialText.append(instruction);
View bg = v.findViewById(R.id.image_background);
if (bg != null) {
bg.setVisibility(GONE);
}
yield v;
}
default -> new View(context);
};
}
private static CharSequence getShortcutInstruction(
@NonNull Context context, @UserShortcutType int shortcutType, int buttonMode,
@NonNull CharSequence featureName, boolean inSetupWizard) {
return switch (shortcutType) {
case HARDWARE -> context.getText(R.string.accessibility_tutorial_dialog_message_volume);
case SOFTWARE -> getSoftwareInstruction(context, buttonMode);
case GESTURE -> StringUtil.getIcuPluralsString(
context,
AccessibilityUtil.isTouchExploreEnabled(context) ? 3 : 2,
R.string.accessibility_tutorial_dialog_gesture_shortcut_instruction);
case TRIPLETAP -> context.getString(
R.string.accessibility_tutorial_dialog_tripletap_instruction, 3);
case TWOFINGER_DOUBLETAP -> context.getString(
R.string.accessibility_tutorial_dialog_twofinger_doubletap_instruction, 2);
case QUICK_SETTINGS -> getQuickSettingsInstruction(context, featureName, inSetupWizard);
default -> "";
};
}
@SuppressLint("SwitchIntDef")
private static TutorialPage createShortcutTutorialPage(
@NonNull Context context, @UserShortcutType int shortcutType, int buttonMode,
@NonNull CharSequence featureName, boolean inSetupWizard) {
final ImageView indicatorIcon =
createImageView(context, R.drawable.ic_accessibility_page_indicator);
indicatorIcon.setEnabled(false);
return new TutorialPage(type, title, image, indicatorIcon, tutorialText);
return new TutorialPage(shortcutType,
getShortcutTitle(context, shortcutType, buttonMode),
getShortcutImage(context, shortcutType, buttonMode),
createImageView(context, R.drawable.ic_accessibility_page_indicator),
getShortcutInstruction(
context, shortcutType, buttonMode, featureName, inSetupWizard));
}
/**
@@ -495,81 +489,54 @@ public final class AccessibilityShortcutsTutorial {
static List<TutorialPage> createShortcutTutorialPages(
@NonNull Context context, int shortcutTypes, @NonNull CharSequence featureName,
boolean inSetupWizard) {
// LINT.IfChange(shortcut_type_ui_order)
final List<TutorialPage> tutorialPages = new ArrayList<>();
if (android.view.accessibility.Flags.a11yQsShortcut()) {
if ((shortcutTypes & QUICK_SETTINGS)
== QUICK_SETTINGS) {
tutorialPages.add(
createQuickSettingsTutorialPage(context, featureName, inSetupWizard));
int buttonMode = ShortcutUtils.getButtonMode(context, context.getUserId());
for (int shortcutType: AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
if ((shortcutTypes & shortcutType) == 0) {
continue;
}
tutorialPages.add(
createShortcutTutorialPage(
context, shortcutType, buttonMode, featureName, inSetupWizard));
}
if ((shortcutTypes & SOFTWARE) == SOFTWARE) {
tutorialPages.add(createSoftwareTutorialPage(context));
}
if ((shortcutTypes & HARDWARE) == HARDWARE) {
tutorialPages.add(createHardwareTutorialPage(context));
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if ((shortcutTypes & TWOFINGER_DOUBLETAP)
== TWOFINGER_DOUBLETAP) {
tutorialPages.add(createTwoFingerTripleTapTutorialPage(context));
}
}
if ((shortcutTypes & TRIPLETAP) == TRIPLETAP) {
tutorialPages.add(createTripleTapTutorialPage(context));
}
// LINT.ThenChange(/res/xml/accessibility_edit_shortcuts.xml:shortcut_type_ui_order)
return tutorialPages;
}
private static View createSoftwareImage(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
return createIllustrationViewWithImageRawResource(
context, R.raw.accessibility_shortcut_type_fab);
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.drawable.accessibility_shortcut_type_gesture_touch_explore_on
: R.drawable.accessibility_shortcut_type_gesture;
} else {
resId = R.drawable.accessibility_shortcut_type_navbar;
}
return createIllustrationView(context, resId);
private static View createSoftwareImage(Context context, int buttonMode) {
return switch(buttonMode) {
case ACCESSIBILITY_BUTTON_MODE_GESTURE ->
createIllustrationView(context,
AccessibilityUtil.isTouchExploreEnabled(context)
? R.drawable
.accessibility_shortcut_type_gesture_touch_explore_on
: R.drawable.accessibility_shortcut_type_gesture);
case ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU ->
createIllustrationViewWithImageRawResource(
context, R.raw.accessibility_shortcut_type_fab);
default -> createIllustrationView(
context, R.drawable.accessibility_shortcut_type_navbar);
};
}
private static CharSequence getSoftwareTitle(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.string.accessibility_tutorial_dialog_title_button;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = R.string.accessibility_tutorial_dialog_title_gesture;
} else {
resId = R.string.accessibility_tutorial_dialog_title_button;
}
return context.getText(resId);
private static CharSequence getSoftwareTitle(Context context, int buttonMode) {
return context.getText(buttonMode == ACCESSIBILITY_BUTTON_MODE_GESTURE
? R.string.accessibility_tutorial_dialog_title_gesture
: R.string.accessibility_tutorial_dialog_title_button);
}
private static CharSequence getSoftwareInstruction(Context context) {
final SpannableStringBuilder sb = new SpannableStringBuilder();
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
final int resId = R.string.accessibility_tutorial_dialog_message_floating_button;
sb.append(context.getText(resId));
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
final int numFingers = AccessibilityUtil.isTouchExploreEnabled(context) ? 3 : 2;
sb.append(StringUtil.getIcuPluralsString(
private static CharSequence getSoftwareInstruction(Context context, int buttonMode) {
return switch(buttonMode) {
case ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU -> context.getText(
R.string.accessibility_tutorial_dialog_message_floating_button);
case ACCESSIBILITY_BUTTON_MODE_GESTURE -> StringUtil.getIcuPluralsString(
context,
numFingers,
R.string.accessibility_tutorial_dialog_gesture_shortcut_instruction));
} else {
final int resId = R.string.accessibility_tutorial_dialog_message_button;
sb.append(getSoftwareInstructionWithIcon(context, context.getText(resId)));
}
return sb;
AccessibilityUtil.isTouchExploreEnabled(context) ? 3 : 2,
R.string.accessibility_tutorial_dialog_gesture_shortcut_instruction);
default -> getSoftwareInstructionWithIcon(context,
context.getText(R.string.accessibility_tutorial_dialog_message_button));
};
}
private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {
@@ -590,6 +557,24 @@ public final class AccessibilityShortcutsTutorial {
return spannableInstruction;
}
private static CharSequence getQuickSettingsInstruction(
Context context, CharSequence featureName, boolean inSetupWizard) {
Map<String, Object> arguments = new ArrayMap<>();
arguments.put("count",
AccessibilityUtil.isTouchExploreEnabled(context) ? 2 : 1);
arguments.put("featureName", featureName);
final CharSequence pluralsString = StringUtil.getIcuPluralsString(
context, arguments,
R.string.accessibility_tutorial_dialog_message_quick_setting);
final SpannableStringBuilder tutorialText = new SpannableStringBuilder();
if (inSetupWizard) {
tutorialText.append(context.getText(R.string
.accessibility_tutorial_dialog_shortcut_unavailable_in_suw))
.append("\n\n");
}
return tutorialText.append(pluralsString);
}
private static class TutorialPage {
private final int mType;
private final CharSequence mTitle;

View File

@@ -22,10 +22,12 @@ import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
@@ -33,6 +35,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Rect;
import android.icu.text.CaseMap;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
@@ -49,14 +52,27 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.settings.R;
import com.android.settings.utils.LocaleUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;
import java.util.StringJoiner;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/** Provides utility methods to accessibility settings only. */
public final class AccessibilityUtil {
// LINT.IfChange(shortcut_type_ui_order)
static final int[] SHORTCUTS_ORDER_IN_UI = {
QUICK_SETTINGS,
SOFTWARE, // FAB displays before gesture. Navbar displays without gesture.
GESTURE,
HARDWARE,
TWOFINGER_DOUBLETAP,
TRIPLETAP
};
// LINT.ThenChange(/res/xml/accessibility_edit_shortcuts.xml:shortcut_type_ui_order)
private AccessibilityUtil(){}
@@ -139,8 +155,8 @@ public final class AccessibilityUtil {
/** Determines if a gesture navigation bar is being used. */
public static boolean isGestureNavigateEnabled(Context context) {
return context.getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode)
return Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.NAVIGATION_MODE, -1)
== NAV_BAR_MODE_GESTURAL;
}
@@ -178,153 +194,6 @@ public final class AccessibilityUtil {
: AccessibilityServiceFragmentType.TOGGLE;
}
/**
* Opts in component name into multiple {@code shortcutTypes} colon-separated string in
* Settings.
*
* @param context The current context.
* @param shortcutTypes A combination of {@link UserShortcutType}.
* @param componentName The component name that need to be opted in Settings.
*/
static void optInAllValuesToSettings(Context context, int shortcutTypes,
@NonNull ComponentName componentName) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
AccessibilityManager a11yManager = context.getSystemService(AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(
/* enable= */ true,
shortcutTypes,
Set.of(componentName.flattenToString()),
UserHandle.myUserId()
);
}
return;
}
if ((shortcutTypes & SOFTWARE) == SOFTWARE) {
optInValueToSettings(context, SOFTWARE, componentName);
}
if (((shortcutTypes & HARDWARE) == HARDWARE)) {
optInValueToSettings(context, HARDWARE, componentName);
}
}
/**
* Opts in component name into {@code shortcutType} colon-separated string in Settings.
*
* @param context The current context.
* @param shortcutType The preferred shortcut type user selected.
* @param componentName The component name that need to be opted in Settings.
*/
@VisibleForTesting
static void optInValueToSettings(Context context, @UserShortcutType int shortcutType,
@NonNull ComponentName componentName) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
AccessibilityManager a11yManager = context.getSystemService(AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(
/* enable= */ true,
shortcutType,
Set.of(componentName.flattenToString()),
UserHandle.myUserId()
);
}
return;
}
final String targetKey = convertKeyFromSettings(shortcutType);
final String targetString = Settings.Secure.getString(context.getContentResolver(),
targetKey);
if (hasValueInSettings(context, shortcutType, componentName)) {
return;
}
final StringJoiner joiner = new StringJoiner(String.valueOf(COMPONENT_NAME_SEPARATOR));
if (!TextUtils.isEmpty(targetString)) {
joiner.add(targetString);
}
joiner.add(componentName.flattenToString());
Settings.Secure.putString(context.getContentResolver(), targetKey, joiner.toString());
}
/**
* Opts out component name into multiple {@code shortcutTypes} colon-separated string in
* Settings.
*
* @param context The current context.
* @param shortcutTypes A combination of {@link UserShortcutType}.
* @param componentName The component name that need to be opted out from Settings.
*/
static void optOutAllValuesFromSettings(Context context, int shortcutTypes,
@NonNull ComponentName componentName) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
AccessibilityManager a11yManager = context.getSystemService(AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(
/* enable= */ false,
shortcutTypes,
Set.of(componentName.flattenToString()),
UserHandle.myUserId()
);
}
return;
}
if ((shortcutTypes & SOFTWARE) == SOFTWARE) {
optOutValueFromSettings(context, SOFTWARE, componentName);
}
if (((shortcutTypes & HARDWARE) == HARDWARE)) {
optOutValueFromSettings(context, HARDWARE, componentName);
}
}
/**
* Opts out component name into {@code shortcutType} colon-separated string in Settings.
*
* @param context The current context.
* @param shortcutType The preferred shortcut type user selected.
* @param componentName The component name that need to be opted out from Settings.
*/
@VisibleForTesting
static void optOutValueFromSettings(Context context, @UserShortcutType int shortcutType,
@NonNull ComponentName componentName) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
AccessibilityManager a11yManager = context.getSystemService(AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(
/* enable= */ false,
shortcutType,
Set.of(componentName.flattenToString()),
UserHandle.myUserId()
);
}
return;
}
final StringJoiner joiner = new StringJoiner(String.valueOf(COMPONENT_NAME_SEPARATOR));
final String targetKey = convertKeyFromSettings(shortcutType);
final String targetString = Settings.Secure.getString(context.getContentResolver(),
targetKey);
if (TextUtils.isEmpty(targetString)) {
return;
}
sStringColonSplitter.setString(targetString);
while (sStringColonSplitter.hasNext()) {
final String name = sStringColonSplitter.next();
if (TextUtils.isEmpty(name) || (componentName.flattenToString()).equals(name)) {
continue;
}
joiner.add(name);
}
Settings.Secure.putString(context.getContentResolver(), targetKey, joiner.toString());
}
/**
* Returns if component name existed in one of {@code shortcutTypes} string in Settings.
*
@@ -335,22 +204,18 @@ public final class AccessibilityUtil {
*/
static boolean hasValuesInSettings(Context context, int shortcutTypes,
@NonNull ComponentName componentName) {
boolean exist = false;
if ((shortcutTypes & SOFTWARE) == SOFTWARE) {
exist = hasValueInSettings(context, SOFTWARE, componentName);
}
if (((shortcutTypes & HARDWARE) == HARDWARE)) {
exist |= hasValueInSettings(context, HARDWARE, componentName);
}
if (android.view.accessibility.Flags.a11yQsShortcut()) {
if ((shortcutTypes & QUICK_SETTINGS)
== QUICK_SETTINGS) {
exist |= hasValueInSettings(context, QUICK_SETTINGS,
componentName);
for (int shortcutType : AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
if (!android.provider.Flags.a11yStandaloneGestureEnabled()) {
if ((shortcutType & GESTURE) == GESTURE) {
continue;
}
}
if ((shortcutTypes & shortcutType) == shortcutType
&& hasValueInSettings(context, shortcutType, componentName)) {
return true;
}
}
return exist;
return false;
}
/**
@@ -360,33 +225,19 @@ public final class AccessibilityUtil {
* @param shortcutType The preferred shortcut type user selected.
* @param componentName The component name that need to be checked existed in Settings.
* @return {@code true} if componentName existed in Settings.
*
* @deprecated use
* {@link ShortcutUtils#isShortcutContained(Context, int, String)} instead.
*
* (TODO 367414968: finish removal.)
*/
@Deprecated
@VisibleForTesting
static boolean hasValueInSettings(Context context, @UserShortcutType int shortcutType,
@NonNull ComponentName componentName) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
return ShortcutUtils.getShortcutTargetsFromSettings(
context, shortcutType, UserHandle.myUserId()
).contains(componentName.flattenToString());
}
final String targetKey = convertKeyFromSettings(shortcutType);
final String targetString = Settings.Secure.getString(context.getContentResolver(),
targetKey);
if (TextUtils.isEmpty(targetString)) {
return false;
}
sStringColonSplitter.setString(targetString);
while (sStringColonSplitter.hasNext()) {
final String name = sStringColonSplitter.next();
if ((componentName.flattenToString()).equals(name)) {
return true;
}
}
return false;
return ShortcutUtils.getShortcutTargetsFromSettings(
context, shortcutType, UserHandle.myUserId()
).contains(componentName.flattenToString());
}
/**
@@ -400,15 +251,14 @@ public final class AccessibilityUtil {
static int getUserShortcutTypesFromSettings(Context context,
@NonNull ComponentName componentName) {
int shortcutTypes = DEFAULT;
if (hasValuesInSettings(context, SOFTWARE, componentName)) {
shortcutTypes |= SOFTWARE;
}
if (hasValuesInSettings(context, HARDWARE, componentName)) {
shortcutTypes |= HARDWARE;
}
if (android.view.accessibility.Flags.a11yQsShortcut()) {
if (hasValuesInSettings(context, QUICK_SETTINGS, componentName)) {
shortcutTypes |= QUICK_SETTINGS;
for (int shortcutType : AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
if (!android.provider.Flags.a11yStandaloneGestureEnabled()) {
if ((shortcutType & GESTURE) == GESTURE) {
continue;
}
}
if (hasValueInSettings(context, shortcutType, componentName)) {
shortcutTypes |= shortcutType;
}
}
@@ -420,23 +270,15 @@ public final class AccessibilityUtil {
*
* @param shortcutType The shortcut type.
* @return Mapping key in Settings.
*
* @deprecated use
* {@link ShortcutUtils#convertToKey(int)} instead.
*
* (TODO 367414968: finish removal.)
*/
@Deprecated
static String convertKeyFromSettings(@UserShortcutType int shortcutType) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
return ShortcutUtils.convertToKey(shortcutType);
}
switch (shortcutType) {
case SOFTWARE:
return Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
case HARDWARE:
return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
case TRIPLETAP:
return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
default:
throw new IllegalArgumentException(
"Unsupported userShortcutType " + shortcutType);
}
return ShortcutUtils.convertToKey(shortcutType);
}
/**
@@ -505,4 +347,60 @@ public final class AccessibilityUtil {
Settings.Secure.SKIP_ACCESSIBILITY_SHORTCUT_DIALOG_TIMEOUT_RESTRICTION, /*
true */ 1);
}
/**
* Assembles a localized string describing the provided shortcut types.
*/
public static CharSequence getShortcutSummaryList(Context context, int shortcutTypes) {
final List<CharSequence> list = new ArrayList<>();
for (int shortcutType : AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
if (!android.provider.Flags.a11yStandaloneGestureEnabled()
&& (shortcutType & GESTURE) == GESTURE) {
continue;
}
if (!com.android.server.accessibility.Flags
.enableMagnificationMultipleFingerMultipleTapGesture()
&& (shortcutType & TWOFINGER_DOUBLETAP) == TWOFINGER_DOUBLETAP) {
continue;
}
if ((shortcutTypes & shortcutType) == shortcutType) {
list.add(switch (shortcutType) {
case QUICK_SETTINGS -> context.getText(
R.string.accessibility_feature_shortcut_setting_summary_quick_settings);
case SOFTWARE -> getSoftwareShortcutSummary(context);
case GESTURE -> context.getText(
R.string.accessibility_shortcut_edit_summary_software_gesture);
case HARDWARE -> context.getText(
R.string.accessibility_shortcut_hardware_keyword);
case TWOFINGER_DOUBLETAP -> context.getString(
R.string.accessibility_shortcut_two_finger_double_tap_keyword, 2);
case TRIPLETAP -> context.getText(
R.string.accessibility_shortcut_triple_tap_keyword);
default -> "";
});
}
}
list.sort(CharSequence::compare);
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
null, LocaleUtils.getConcatenatedString(list));
}
@VisibleForTesting
static CharSequence getSoftwareShortcutSummary(Context context) {
if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
return context.getText(R.string.accessibility_shortcut_edit_summary_software);
}
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
} else {
resId = R.string.accessibility_shortcut_edit_summary_software;
}
return context.getText(resId);
}
}

View File

@@ -1,98 +0,0 @@
/*
* Copyright (C) 2024 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 android.app.Activity.RESULT_CANCELED;
import android.app.settings.SettingsEnums;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.display.AutoBrightnessSettings;
import com.android.settingslib.Utils;
import com.android.settingslib.widget.FooterPreference;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupdesign.GlifPreferenceLayout;
/**
* Fragment for adaptive brightness settings in the SetupWizard.
*/
public class AutoBrightnessPreferenceFragmentForSetupWizard extends AutoBrightnessSettings {
private static final String FOOTER_PREFERENCE_KEY = "auto_brightness_footer";
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
updateFooterContentDescription();
if (view instanceof GlifPreferenceLayout) {
final GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
final String title = getContext().getString(
R.string.auto_brightness_title);
final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility);
icon.setTintList(Utils.getColorAttr(getContext(), android.R.attr.colorPrimary));
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
/* description= */ null, icon);
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
AccessibilitySetupWizardUtils.setPrimaryButton(getContext(), mixin, R.string.done,
() -> {
setResult(RESULT_CANCELED);
finish();
});
}
}
@NonNull
@Override
public RecyclerView onCreateRecyclerView(@NonNull LayoutInflater inflater,
@NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) {
if (parent instanceof GlifPreferenceLayout) {
final GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
}
return super.onCreateRecyclerView(inflater, parent, savedInstanceState);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SUW_ACCESSIBILITY_AUTO_BRIGHTNESS;
}
private void updateFooterContentDescription() {
final PreferenceScreen screen = getPreferenceScreen();
final FooterPreference footerPreference = screen.findPreference(FOOTER_PREFERENCE_KEY);
if (footerPreference != null) {
String title = getString(R.string.auto_brightness_content_description_title);
final StringBuilder sb = new StringBuilder();
sb.append(title).append("\n\n").append(footerPreference.getTitle());
footerPreference.setContentDescription(sb);
}
}
}

View File

@@ -16,18 +16,11 @@
package com.android.settings.accessibility;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.PluralsRes;
import android.annotation.StringRes;
import android.content.Context;
import android.content.res.Resources;
import com.android.settings.R;
import com.android.settingslib.utils.StringUtil;
import java.lang.annotation.Retention;
import java.util.HashMap;
import java.util.Map;
@@ -52,16 +45,6 @@ public final class AutoclickUtils {
*/
static final int AUTOCLICK_DELAY_STEP = 100;
@Retention(SOURCE)
@IntDef({
Quantity.ONE,
Quantity.FEW
})
private @interface Quantity {
int ONE = 1;
int FEW = 3;
}
/**
* Gets string that should be used for provided autoclick delay.
*
@@ -73,13 +56,12 @@ public final class AutoclickUtils {
*/
public static CharSequence getAutoclickDelaySummary(Context context,
@StringRes int id, int delayMillis) {
final int quantity = (delayMillis == 1000) ? Quantity.ONE : Quantity.FEW;
final float delaySecond = (float) delayMillis / 1000;
// Only show integer when delay time is 1.
final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f";
Map<String, Object> arguments = new HashMap<>();
arguments.put("count", quantity);
arguments.put("count", delaySecond);
arguments.put("time", String.format(decimalFormat, delaySecond));
return StringUtil.getIcuPluralsString(context, arguments, id);
}

View File

@@ -18,11 +18,14 @@ package com.android.settings.accessibility;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.bluetooth.BluetoothDeviceUpdater;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment;
@@ -32,6 +35,9 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.search.SearchIndexableRaw;
import java.util.List;
/**
* Controller to update the {@link androidx.preference.PreferenceCategory} for all
@@ -44,6 +50,7 @@ public class AvailableHearingDevicePreferenceController extends
BluetoothCallback {
private static final String TAG = "AvailableHearingDevicePreferenceController";
private static final String SEARCH_DATA_KEY_PREFIX = "a11y_available_hearing_device";
private BluetoothDeviceUpdater mAvailableHearingDeviceUpdater;
private final LocalBluetoothManager mLocalBluetoothManager;
@@ -56,6 +63,14 @@ public class AvailableHearingDevicePreferenceController extends
context);
}
@VisibleForTesting
void init(AvailableHearingDeviceUpdater availableHearingDeviceUpdater) {
if (mAvailableHearingDeviceUpdater != null) {
throw new IllegalStateException("Should not call init() more than 1 time.");
}
mAvailableHearingDeviceUpdater = availableHearingDeviceUpdater;
}
/**
* Initializes objects in this controller. Need to call this before onStart().
*
@@ -107,4 +122,34 @@ public class AvailableHearingDevicePreferenceController extends
getMetricsCategory());
}
}
@Override
public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) {
if (Flags.fixA11ySettingsSearch()) {
if (mLocalBluetoothManager == null) {
Log.d(TAG, "Bluetooth is not supported");
return;
}
for (CachedBluetoothDevice cachedDevice :
mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) {
if (!AvailableHearingDeviceUpdater.isAvailableHearingDevice(cachedDevice)) {
continue;
}
SearchIndexableRaw data = new SearchIndexableRaw(mContext);
// Include the identity address and add prefix to ensure the key is unique and
// distinguish from Bluetooth's connected devices.
data.key = SEARCH_DATA_KEY_PREFIX
+ cachedDevice.getName() + cachedDevice.getIdentityAddress();
data.title = cachedDevice.getName();
data.summaryOn = mContext.getString(R.string.accessibility_hearingaid_title);
data.screenTitle = mContext.getString(R.string.accessibility_hearingaid_title);
rawData.add(data);
}
} else {
super.updateDynamicRawDataToIndex(rawData);
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import com.android.settings.bluetooth.AvailableMediaBluetoothDeviceUpdater;
@@ -34,10 +35,16 @@ public class AvailableHearingDeviceUpdater extends AvailableMediaBluetoothDevice
super(context, devicePreferenceCallback, metricsCategory);
}
static boolean isAvailableHearingDevice(CachedBluetoothDevice cachedDevice) {
final BluetoothDevice device = cachedDevice.getDevice();
return cachedDevice.isHearingAidDevice()
&& device.getBondState() == BluetoothDevice.BOND_BONDED
&& device.isConnected();
}
@Override
public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
return cachedDevice.isHearingAidDevice()
&& isDeviceConnected(cachedDevice)
return isAvailableHearingDevice(cachedDevice)
&& isDeviceInCachedDevicesList(cachedDevice);
}

View File

@@ -89,7 +89,7 @@ public class BalanceSeekBar extends SeekBar {
Settings.System.MASTER_BALANCE, balance, UserHandle.USER_CURRENT);
}
final int max = getMax();
if (Flags.audioBalanceStateDescription() && max > 0) {
if (max > 0) {
seekBar.setStateDescription(createStateDescription(mContext,
R.string.audio_seek_bar_state_left_first,
R.string.audio_seek_bar_state_right_first,

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2024 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 com.android.settings.core.BasePreferenceController;
public class BaseCaptioningCustomController extends BasePreferenceController {
protected final CaptionHelper mCaptionHelper;
public BaseCaptioningCustomController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return mCaptionHelper.getCustomCaptionAvailability();
}
}

View File

@@ -18,6 +18,8 @@ package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import android.content.ContentResolver;
import android.content.Context;
@@ -211,4 +213,14 @@ public class CaptionHelper {
public Locale getLocale() {
return mCaptioningManager.getLocale();
}
/** Returns availability for custom caption preferences, depending on current user style. */
public int getCustomCaptionAvailability() {
if (com.android.settings.accessibility.Flags.fixA11ySettingsSearch()) {
return (getRawUserStyle() == CaptionStyle.PRESET_CUSTOM)
? AVAILABLE : AVAILABLE_UNSEARCHABLE;
} else {
return AVAILABLE;
}
}
}

View File

@@ -25,23 +25,15 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning background color. */
public class CaptioningBackgroundColorController extends BasePreferenceController
public class CaptioningBackgroundColorController extends BaseCaptioningCustomController
implements OnValueChangedListener {
private final CaptionHelper mCaptionHelper;
private int mCachedNonDefaultOpacity = CaptionStyle.COLOR_UNSPECIFIED;
public CaptioningBackgroundColorController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -23,22 +23,13 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning background opacity. */
public class CaptioningBackgroundOpacityController extends BasePreferenceController
public class CaptioningBackgroundOpacityController extends BaseCaptioningCustomController
implements OnValueChangedListener {
private final CaptionHelper mCaptionHelper;
public CaptioningBackgroundOpacityController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Handler;
@@ -27,7 +28,6 @@ import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -36,11 +36,11 @@ import java.util.Arrays;
import java.util.List;
/** Preference controller for captioning custom visibility. */
public class CaptioningCustomController extends BasePreferenceController
public class CaptioningCustomController extends BaseCaptioningCustomController
implements LifecycleObserver, OnStart, OnStop {
@Nullable
private Preference mCustom;
private final CaptionHelper mCaptionHelper;
private final ContentResolver mContentResolver;
@VisibleForTesting
AccessibilitySettingsContentObserver mSettingsContentObserver;
@@ -50,32 +50,31 @@ public class CaptioningCustomController extends BasePreferenceController
);
public CaptioningCustomController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
mContentResolver = context.getContentResolver();
mSettingsContentObserver = new AccessibilitySettingsContentObserver(
new Handler(Looper.getMainLooper()));
mSettingsContentObserver.registerKeysToObserverCallback(CAPTIONING_FEATURE_KEYS,
key -> refreshShowingCustom());
this(context, preferenceKey,
new AccessibilitySettingsContentObserver(new Handler(Looper.getMainLooper())));
}
@VisibleForTesting
CaptioningCustomController(Context context, String preferenceKey,
CaptioningCustomController(
Context context, String preferenceKey,
AccessibilitySettingsContentObserver contentObserver) {
this(context, preferenceKey);
super(context, preferenceKey);
mContentResolver = context.getContentResolver();
mSettingsContentObserver = contentObserver;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
mSettingsContentObserver.registerKeysToObserverCallback(CAPTIONING_FEATURE_KEYS, key -> {
if (mCustom != null) {
mCustom.setVisible(shouldShowPreference());
}
});
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mCustom = screen.findPreference(getPreferenceKey());
refreshShowingCustom();
if (mCustom != null) {
mCustom.setVisible(shouldShowPreference());
}
}
@Override
@@ -88,9 +87,7 @@ public class CaptioningCustomController extends BasePreferenceController
mSettingsContentObserver.unregister(mContentResolver);
}
private void refreshShowingCustom() {
final boolean isCustomPreset =
mCaptionHelper.getRawUserStyle() == CaptioningManager.CaptionStyle.PRESET_CUSTOM;
mCustom.setVisible(isCustomPreset);
private boolean shouldShowPreference() {
return mCaptionHelper.getRawUserStyle() == CaptioningManager.CaptionStyle.PRESET_CUSTOM;
}
}

View File

@@ -23,22 +23,13 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning edge color. */
public class CaptioningEdgeColorController extends BasePreferenceController
public class CaptioningEdgeColorController extends BaseCaptioningCustomController
implements OnValueChangedListener {
private final CaptionHelper mCaptionHelper;
public CaptioningEdgeColorController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -21,22 +21,13 @@ import android.content.Context;
import androidx.preference.PreferenceScreen;
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning edge type. */
public class CaptioningEdgeTypeController extends BasePreferenceController
public class CaptioningEdgeTypeController extends BaseCaptioningCustomController
implements OnValueChangedListener {
private final CaptionHelper mCaptionHelper;
public CaptioningEdgeTypeController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -24,23 +24,15 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning foreground color. */
public class CaptioningForegroundColorController extends BasePreferenceController
public class CaptioningForegroundColorController extends BaseCaptioningCustomController
implements OnValueChangedListener {
private final CaptionHelper mCaptionHelper;
private int mCachedNonDefaultOpacity = CaptionStyle.COLOR_UNSPECIFIED;
public CaptioningForegroundColorController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -23,22 +23,13 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning foreground opacity. */
public class CaptioningForegroundOpacityController extends BasePreferenceController
public class CaptioningForegroundOpacityController extends BaseCaptioningCustomController
implements OnValueChangedListener {
private final CaptionHelper mCaptionHelper;
public CaptioningForegroundOpacityController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -23,22 +23,12 @@ import android.view.accessibility.CaptioningManager.CaptionStyle;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning type face. */
public class CaptioningTypefaceController extends BasePreferenceController
public class CaptioningTypefaceController extends BaseCaptioningCustomController
implements Preference.OnPreferenceChangeListener {
private final CaptionHelper mCaptionHelper;
public CaptioningTypefaceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -25,23 +25,15 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning window color. */
public class CaptioningWindowColorController extends BasePreferenceController
public class CaptioningWindowColorController extends BaseCaptioningCustomController
implements OnValueChangedListener {
private final CaptionHelper mCaptionHelper;
private int mCachedNonDefaultOpacity = CaptionStyle.COLOR_UNSPECIFIED;
public CaptioningWindowColorController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -23,22 +23,13 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
import com.android.settings.core.BasePreferenceController;
/** Preference controller for captioning window opacity. */
public class CaptioningWindowOpacityController extends BasePreferenceController
public class CaptioningWindowOpacityController extends BaseCaptioningCustomController
implements OnValueChangedListener {
private final CaptionHelper mCaptionHelper;
public CaptioningWindowOpacityController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptionHelper = new CaptionHelper(context);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override

View File

@@ -17,12 +17,15 @@
package com.android.settings.accessibility;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.hardware.display.ColorDisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.view.accessibility.Flags;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.TwoStatePreference;
@@ -47,7 +50,6 @@ public class ColorAndMotionFragment extends DashboardFragment {
// Preferences
private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN = "daltonizer_preference";
private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations";
private static final String TOGGLE_LARGE_POINTER_ICON = "toggle_large_pointer_icon";
@VisibleForTesting
static final String TOGGLE_FORCE_INVERT = "toggle_force_invert";
@@ -74,9 +76,7 @@ public class ColorAndMotionFragment extends DashboardFragment {
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
if (android.view.accessibility.Flags.a11yQsShortcut()) {
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
}
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
if (Flags.forceInvertColor()) {
mShortcutFeatureKeys.add(ToggleForceInvertPreferenceController.SETTINGS_KEY);
}
@@ -122,7 +122,7 @@ public class ColorAndMotionFragment extends DashboardFragment {
mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN);
// Disable animation.
mToggleDisableAnimationsPreference = findPreference(TOGGLE_DISABLE_ANIMATIONS);
mToggleDisableAnimationsPreference = findPreference(RemoveAnimationsPreference.KEY);
// Large pointer icon.
mToggleLargePointerIconPreference = findPreference(TOGGLE_LARGE_POINTER_ICON);
@@ -148,6 +148,12 @@ public class ColorAndMotionFragment extends DashboardFragment {
}
}
@Nullable
@Override
public String getPreferenceScreenBindingKey(@NonNull Context context) {
return ColorAndMotionScreen.KEY;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_color_and_motion);
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2024 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 com.android.settings.R
import com.android.settings.Settings.ColorAndMotionActivity
import com.android.settings.flags.Flags
import com.android.settings.utils.makeLaunchIntent
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
@ProvidePreferenceScreen
class ColorAndMotionScreen : PreferenceScreenCreator {
override val key: String
get() = KEY
override val title: Int
get() = R.string.accessibility_color_and_motion_title
override val icon: Int
get() = R.drawable.ic_color_and_motion
override fun isFlagEnabled(context: Context) = Flags.catalystAccessibilityColorAndMotion()
override fun hasCompleteHierarchy(): Boolean = false
override fun fragmentClass() = ColorAndMotionFragment::class.java
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(this) {
+RemoveAnimationsPreference()
}
override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
makeLaunchIntent(context, ColorAndMotionActivity::class.java, metadata?.key)
companion object {
const val KEY = "accessibility_color_and_motion"
}
}

View File

@@ -37,6 +37,7 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.Arrays;
import java.util.List;
// LINT.IfChange
/** A toggle preference controller for disable animations. */
public class DisableAnimationsPreferenceController extends TogglePreferenceController implements
LifecycleObserver, OnStart, OnStop {
@@ -123,3 +124,4 @@ public class DisableAnimationsPreferenceController extends TogglePreferenceContr
mContentResolver.unregisterContentObserver(mSettingsContentObserver);
}
}
// LINT.ThenChange(src/com/android/settings/accessibility/RemoveAnimationsPreference.kt)

View File

@@ -35,7 +35,7 @@ class DisplaySizeData extends PreviewSizeData<Integer> {
super(context);
mDensity = new DisplayDensityUtils(getContext());
final int initialIndex = mDensity.getCurrentIndexForDefaultDisplay();
final int initialIndex = mDensity.getCurrentIndex();
if (initialIndex < 0) {
// Failed to obtain default density, which means we failed to
// connect to the window manager service. Just use the current
@@ -46,9 +46,9 @@ class DisplaySizeData extends PreviewSizeData<Integer> {
setInitialIndex(0);
setValues(Collections.singletonList(densityDpi));
} else {
setDefaultValue(mDensity.getDefaultDensityForDefaultDisplay());
setDefaultValue(mDensity.getDefaultDensity());
setInitialIndex(initialIndex);
setValues(Arrays.stream(mDensity.getDefaultDisplayDensityValues()).boxed()
setValues(Arrays.stream(mDensity.getValues()).boxed()
.collect(Collectors.toList()));
}
}

View File

@@ -18,8 +18,6 @@ package com.android.settings.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
import com.android.settingslib.accessibility.AccessibilityUtils;
/**
* Fragment that does not have toggle bar to turn on service to use.
*
@@ -37,29 +35,4 @@ public class InvisibleToggleAccessibilityServicePreferenceFragment extends
super.onInstallSwitchPreferenceToggleSwitch();
mToggleServiceSwitchPreference.setVisible(false);
}
/**
* {@inheritDoc}
*
* Enables accessibility service only when user had allowed permission. Disables
* accessibility service when shortcutPreference is unchecked.
*/
@Override
public void onToggleClicked(ShortcutPreference preference) {
super.onToggleClicked(preference);
boolean enabled = getArguments().getBoolean(AccessibilitySettings.EXTRA_CHECKED)
&& preference.isChecked();
AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName, enabled);
}
/**
* {@inheritDoc}
*
* Enables accessibility service when user clicks permission allow button.
*/
@Override
void onAllowButtonFromShortcutToggleClicked() {
super.onAllowButtonFromShortcutToggleClicked();
AccessibilityUtils.setAccessibilityServiceState(getContext(), mComponentName, true);
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (C) 2024 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.hardware.input.InputSettings;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
import java.util.List;
/**
* A toggle preference controller for keyboard bounce key.
*/
public class KeyboardBounceKeyPreferenceController extends TogglePreferenceController {
private static final String TAG = "BounceKeyPrefController";
static final String PREF_KEY = "toggle_keyboard_bounce_keys";
public KeyboardBounceKeyPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return InputSettings.isAccessibilityBounceKeysFeatureEnabled()
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public boolean isChecked() {
return InputSettings.isAccessibilityBounceKeysEnabled(mContext);
}
@Override
public boolean setChecked(boolean isChecked) {
InputSettings.setAccessibilityBounceKeysThreshold(mContext,
isChecked ? PhysicalKeyboardFragment.BOUNCE_KEYS_THRESHOLD
: 0);
return true;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public void updateNonIndexableKeys(@NonNull List<String> keys) {
super.updateNonIndexableKeys(keys);
if (Flags.fixA11ySettingsSearch() && !AccessibilitySettings.isAnyHardKeyboardsExist()) {
if (keys.contains(getPreferenceKey())) {
Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list.");
return;
}
keys.add(getPreferenceKey());
}
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright (C) 2024 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.hardware.input.InputSettings;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
import java.util.List;
/**
* A toggle preference controller for keyboard slow key.
*/
public class KeyboardSlowKeyPreferenceController extends TogglePreferenceController {
private static final String TAG = "SlowKeyPrefController";
static final String PREF_KEY = "toggle_keyboard_slow_keys";
public KeyboardSlowKeyPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public boolean isChecked() {
return InputSettings.isAccessibilitySlowKeysEnabled(mContext);
}
@Override
public boolean setChecked(boolean isChecked) {
InputSettings.setAccessibilitySlowKeysThreshold(mContext,
isChecked ? PhysicalKeyboardFragment.SLOW_KEYS_THRESHOLD
: 0);
return true;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public void updateNonIndexableKeys(@NonNull List<String> keys) {
super.updateNonIndexableKeys(keys);
if (Flags.fixA11ySettingsSearch() && !AccessibilitySettings.isAnyHardKeyboardsExist()) {
if (keys.contains(getPreferenceKey())) {
Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list.");
return;
}
keys.add(getPreferenceKey());
}
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) 2024 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.hardware.input.InputSettings;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import java.util.List;
/**
* A toggle preference controller for keyboard sticky key.
*/
public class KeyboardStickyKeyPreferenceController extends TogglePreferenceController {
private static final String TAG = "StickyKeyPrefController";
static final String PREF_KEY = "toggle_keyboard_sticky_keys";
public KeyboardStickyKeyPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return InputSettings.isAccessibilityStickyKeysFeatureEnabled()
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public boolean isChecked() {
return InputSettings.isAccessibilityStickyKeysEnabled(mContext);
}
@Override
public boolean setChecked(boolean isChecked) {
InputSettings.setAccessibilityStickyKeysEnabled(mContext, isChecked);
return true;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public void updateNonIndexableKeys(@NonNull List<String> keys) {
super.updateNonIndexableKeys(keys);
if (Flags.fixA11ySettingsSearch() && !AccessibilitySettings.isAnyHardKeyboardsExist()) {
if (keys.contains(getPreferenceKey())) {
Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list.");
return;
}
keys.add(getPreferenceKey());
}
}
}

View File

@@ -67,7 +67,7 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
initLaunchPreference();
final View view = super.onCreateView(inflater, container, savedInstanceState);
removePreference(KEY_USE_SERVICE_PREFERENCE);
removePreference(getUseServicePreferenceKey());
return view;
}
@@ -81,7 +81,7 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
super.onProcessArguments(arguments);
mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME);
final ActivityInfo info = getAccessibilityShortcutInfo().getActivityInfo();
mPackageName = info.loadLabel(getPackageManager()).toString();
mFeatureName = info.loadLabel(getPackageManager());
// Settings animated image.
final int animatedImageRes = arguments.getInt(

View File

@@ -24,6 +24,7 @@ import static com.android.settings.accessibility.AccessibilityUtil.getScreenWidt
import static com.google.common.primitives.Ints.max;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Paint.FontMetrics;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
@@ -39,6 +40,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.Utils;
import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
@@ -128,6 +130,8 @@ public final class PaletteListPreference extends Preference {
final List<Integer> paletteColors = getPaletteColors(context);
final List<String> paletteData = getPaletteData(context);
final ColorStateList textColor =
Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
final float textPadding =
context.getResources().getDimension(R.dimen.accessibility_layout_margin_start_end);
final String maxLengthData =
@@ -143,6 +147,7 @@ public final class PaletteListPreference extends Preference {
for (int i = 0; i < paletteData.size(); ++i) {
final TextView textView = new TextView(context);
textView.setText(paletteData.get(i));
textView.setTextColor(textColor);
textView.setHeight(paletteItemHeight);
textView.setPaddingRelative(Math.round(textPadding), 0, 0, 0);
textView.setGravity(Gravity.CENTER_VERTICAL);

View File

@@ -17,7 +17,6 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import android.content.ComponentName;
@@ -25,12 +24,10 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.accessibility.Flags;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.accessibility.util.ShortcutUtils;
@@ -123,12 +120,7 @@ public final class PreferredShortcuts {
public static void updatePreferredShortcutsFromSettings(
@NonNull Context context, @NonNull Set<String> components) {
final Map<Integer, Set<String>> shortcutTypeToTargets = new ArrayMap<>();
for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
if (!Flags.a11yQsShortcut()
&& shortcutType == QUICK_SETTINGS) {
// Skip saving quick setting as preferred shortcut option when flag is not enabled
continue;
}
for (int shortcutType : AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
shortcutTypeToTargets.put(
shortcutType,
ShortcutUtils.getShortcutTargetsFromSettings(

View File

@@ -16,9 +16,6 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
import android.content.ComponentName;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.display.ColorDisplayManager;
@@ -29,12 +26,12 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.server.display.feature.flags.Flags;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
@@ -42,7 +39,7 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
/** PreferenceController that shows the Reduce Bright Colors summary */
public class ReduceBrightColorsPreferenceController
extends AccessibilityQuickSettingsPrimarySwitchPreferenceController
extends TogglePreferenceController
implements LifecycleObserver, OnStart, OnStop {
private ContentObserver mSettingsContentObserver;
private PrimarySwitchPreference mPreference;
@@ -72,7 +69,6 @@ public class ReduceBrightColorsPreferenceController
@Override
public boolean setChecked(boolean isChecked) {
super.setChecked(isChecked);
return mColorDisplayManager.setReduceBrightColorsActivated(isChecked);
}
@@ -125,20 +121,4 @@ public class ReduceBrightColorsPreferenceController
public void onStop() {
mContext.getContentResolver().unregisterContentObserver(mSettingsContentObserver);
}
@Nullable
@Override
protected ComponentName getTileComponentName() {
// TODO: When clean up the feature flag, change the parent class from
// AccessibilityQuickSettingsPrimarySwitchPreferenceController to
// TogglePreferenceController
return android.view.accessibility.Flags.a11yQsShortcut()
? null : REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
}
@Override
CharSequence getTileTooltipContent() {
return mContext.getText(
R.string.accessibility_reduce_bright_colors_auto_added_qs_tooltip_content);
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2024 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.annotation.DrawableRes
import android.content.Context
import android.provider.Settings
import com.android.settings.R
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.datastore.NoOpKeyedObservable
import com.android.settingslib.datastore.SettingsGlobalStore
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.SensitivityLevel
import com.android.settingslib.metadata.SwitchPreference
class RemoveAnimationsPreference :
SwitchPreference(
KEY,
R.string.accessibility_disable_animations,
R.string.accessibility_disable_animations_summary,
),
PreferenceLifecycleProvider {
private var mSettingsKeyedObserver: KeyedObserver<String>? = null
override val icon: Int
@DrawableRes get() = R.drawable.ic_accessibility_animation
override fun onStart(context: PreferenceLifecycleContext) {
val observer = KeyedObserver<String> { _, _ -> context.notifyPreferenceChange(KEY) }
mSettingsKeyedObserver = observer
val storage = SettingsGlobalStore.get(context)
for (key in getAnimationKeys()) {
storage.addObserver(key, observer, HandlerExecutor.main)
}
}
override fun onStop(context: PreferenceLifecycleContext) {
mSettingsKeyedObserver?.let {
val storage = SettingsGlobalStore.get(context)
for (key in getAnimationKeys()) {
storage.removeObserver(key, it)
}
mSettingsKeyedObserver = null
}
}
override fun storage(context: Context): KeyValueStore = RemoveAnimationsStorage(context)
override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
@Suppress("UNCHECKED_CAST")
private class RemoveAnimationsStorage(private val context: Context) :
NoOpKeyedObservable<String>(), KeyValueStore {
override fun contains(key: String) = key == KEY
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
when {
key == KEY && valueType == Boolean::class.javaObjectType ->
!isAnimationEnabled(context) as T
else -> null
}
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
if (key == KEY && value is Boolean) {
setAnimationEnabled(context, !value)
}
}
}
companion object {
// This KEY must match the key used in accessibility_color_and_motion.xml for this
// preference, at least until the entire screen is migrated to Catalyst and that XML
// is deleted. Use any key from the set of 3 toggle animation keys.
const val KEY = Settings.Global.ANIMATOR_DURATION_SCALE
const val ANIMATION_ON_VALUE: Float = 1.0f
const val ANIMATION_OFF_VALUE: Float = 0.0f
fun isAnimationEnabled(context: Context): Boolean {
val storage = SettingsGlobalStore.get(context)
// This pref treats animation as enabled if *any* of the animation types are enabled.
for (animationSetting in getAnimationKeys()) {
val animationValue: Float? = storage.getFloat(animationSetting)
// Animation is enabled by default, so treat null as enabled.
if (animationValue == null || animationValue > ANIMATION_OFF_VALUE) {
return true
}
}
return false
}
fun setAnimationEnabled(context: Context, enabled: Boolean) {
val storage = SettingsGlobalStore.get(context)
val value = if (enabled) ANIMATION_ON_VALUE else ANIMATION_OFF_VALUE
for (animationSetting in getAnimationKeys()) {
storage.setFloat(animationSetting, value)
}
}
fun getAnimationKeys() =
listOf(
Settings.Global.WINDOW_ANIMATION_SCALE,
Settings.Global.TRANSITION_ANIMATION_SCALE,
Settings.Global.ANIMATOR_DURATION_SCALE,
)
}
}

View File

@@ -78,7 +78,6 @@ public class RestrictedPreferenceHelper {
final AccessibilityServiceInfo info = installedServices.get(i);
final ResolveInfo resolveInfo = info.getResolveInfo();
final String packageName = resolveInfo.serviceInfo.packageName;
final ComponentName componentName = new ComponentName(packageName,
resolveInfo.serviceInfo.name);
final boolean serviceEnabled = enabledServices.contains(componentName);
@@ -102,10 +101,6 @@ public class RestrictedPreferenceHelper {
*/
public List<AccessibilityActivityPreference> createAccessibilityActivityPreferenceList(
List<AccessibilityShortcutInfo> installedShortcuts) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedShortcutsSize = installedShortcuts.size();
final List<AccessibilityActivityPreference> preferenceList = new ArrayList<>(
@@ -116,17 +111,12 @@ public class RestrictedPreferenceHelper {
final ActivityInfo activityInfo = info.getActivityInfo();
final ComponentName componentName = info.getComponentName();
final boolean serviceEnabled = enabledServices.contains(componentName);
AccessibilityActivityPreference preference = new AccessibilityActivityPreference(
mContext, componentName.getPackageName(), activityInfo.applicationInfo.uid,
info);
if (Flags.neverRestrictAccessibilityActivity()) {
// Accessibility Activities do not have elevated privileges so restricting
// them based on ECM or device admin does not give any value.
preference.setEnabled(true);
} else {
setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
}
// Accessibility Activities do not have elevated privileges so restricting
// them based on ECM or device admin does not give any value.
preference.setEnabled(true);
preferenceList.add(preference);
}
return preferenceList;

View File

@@ -17,17 +17,26 @@
package com.android.settings.accessibility;
import android.content.Context;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.bluetooth.BluetoothDeviceUpdater;
import com.android.settings.bluetooth.Utils;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.search.SearchIndexableRaw;
import java.util.List;
/**
* Controller to update the {@link androidx.preference.PreferenceCategory} for all
@@ -39,11 +48,23 @@ public class SavedHearingDevicePreferenceController extends
BaseBluetoothDevicePreferenceController implements LifecycleObserver, OnStart, OnResume,
OnStop {
private static final String TAG = "SavedHearingDevicePreferenceController";
private static final String SEARCH_DATA_KEY_PREFIX = "a11y_saved_hearing_device";
private BluetoothDeviceUpdater mSavedHearingDeviceUpdater;
private final LocalBluetoothManager mLocalBluetoothManager;
public SavedHearingDevicePreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey);
mLocalBluetoothManager = Utils.getLocalBluetoothManager(context);
}
@VisibleForTesting
void init(SavedHearingDeviceUpdater savedHearingDeviceUpdater) {
if (mSavedHearingDeviceUpdater != null) {
throw new IllegalStateException("Should not call init() more than 1 time.");
}
mSavedHearingDeviceUpdater = savedHearingDeviceUpdater;
}
/**
@@ -86,4 +107,34 @@ public class SavedHearingDevicePreferenceController extends
mSavedHearingDeviceUpdater.forceUpdate();
}
}
@Override
public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) {
if (Flags.fixA11ySettingsSearch()) {
if (mLocalBluetoothManager == null) {
Log.d(TAG, "Bluetooth is not supported");
return;
}
for (CachedBluetoothDevice cachedDevice :
mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) {
if (!SavedHearingDeviceUpdater.isSavedHearingAidDevice(cachedDevice)) {
continue;
}
SearchIndexableRaw data = new SearchIndexableRaw(mContext);
// Include the identity address and add prefix to ensure the key is unique and
// distinguish from Bluetooth's connected devices.
data.key = SEARCH_DATA_KEY_PREFIX
+ cachedDevice.getName() + cachedDevice.getIdentityAddress();
data.title = cachedDevice.getName();
data.summaryOn = mContext.getString(R.string.accessibility_hearingaid_title);
data.screenTitle = mContext.getString(R.string.accessibility_hearingaid_title);
rawData.add(data);
}
} else {
super.updateDynamicRawDataToIndex(rawData);
}
}
}

View File

@@ -36,14 +36,16 @@ public class SavedHearingDeviceUpdater extends SavedBluetoothDeviceUpdater {
super(context, devicePreferenceCallback, /* showConnectedDevice= */ false, metricsCategory);
}
@Override
public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
static boolean isSavedHearingAidDevice(CachedBluetoothDevice cachedDevice) {
final BluetoothDevice device = cachedDevice.getDevice();
final boolean isSavedHearingAidDevice = cachedDevice.isHearingAidDevice()
return cachedDevice.isHearingAidDevice()
&& device.getBondState() == BluetoothDevice.BOND_BONDED
&& !device.isConnected();
}
return isSavedHearingAidDevice && isDeviceInCachedDevicesList(cachedDevice);
@Override
public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
return isSavedHearingAidDevice(cachedDevice) && isDeviceInCachedDevicesList(cachedDevice);
}
@Override

View File

@@ -30,6 +30,8 @@ import android.view.View;
import android.widget.Toast;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
@@ -64,7 +66,6 @@ public class TextReadingPreferenceFragment extends DashboardFragment {
static final String RESET_KEY = "reset";
static final String PREVIEW_KEY = "preview";
private static final String NEED_RESET_SETTINGS = "need_reset_settings";
private static final String LAST_PREVIEW_INDEX = "last_preview_index";
private static final int UNKNOWN_INDEX = -1;
private FontWeightAdjustmentPreferenceController mFontWeightAdjustmentController;
@@ -109,13 +110,6 @@ public class TextReadingPreferenceFragment extends DashboardFragment {
if (savedInstanceState.getBoolean(NEED_RESET_SETTINGS)) {
mResetStateListeners.forEach(ResetStateListener::resetState);
}
if (savedInstanceState.containsKey(LAST_PREVIEW_INDEX)) {
final int lastPreviewIndex = savedInstanceState.getInt(LAST_PREVIEW_INDEX);
if (lastPreviewIndex != UNKNOWN_INDEX) {
mPreviewController.setCurrentItem(lastPreviewIndex);
}
}
}
}
@@ -250,8 +244,6 @@ public class TextReadingPreferenceFragment extends DashboardFragment {
if (mNeedResetSettings) {
outState.putBoolean(NEED_RESET_SETTINGS, true);
}
outState.putInt(LAST_PREVIEW_INDEX, mPreviewController.getCurrentItem());
}
@Override
@@ -313,4 +305,9 @@ public class TextReadingPreferenceFragment extends DashboardFragment {
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_text_reading_options);
@Override
public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
return TextReadingScreen.KEY;
}
}

View File

@@ -17,6 +17,8 @@
package com.android.settings.accessibility;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
@@ -36,6 +38,7 @@ import com.android.settings.widget.DotsPageIndicator;
* A {@link Preference} that could show the preview related to the text and reading options.
*/
public class TextReadingPreviewPreference extends Preference {
private static final String KEY_LAST_INDEX = "last_preview_index";
private int mCurrentItem;
private int mLastLayerIndex;
private PreviewPagerAdapter mPreviewAdapter;
@@ -98,6 +101,22 @@ public class TextReadingPreviewPreference extends Preference {
updatePagerAndIndicator(viewPager, pageIndicator);
}
@Override
protected Parcelable onSaveInstanceState() {
Bundle state = new Bundle();
state.putParcelable(null, super.onSaveInstanceState());
state.putInt(KEY_LAST_INDEX, getCurrentItem());
return state;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
super.onRestoreInstanceState(bundle.getParcelable(null));
setCurrentItem(bundle.getInt(KEY_LAST_INDEX));
}
/**
* Set the minimum preview layout horizontal inner padding.
*/

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2024 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 com.android.settings.R
import com.android.settings.flags.Flags
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
@ProvidePreferenceScreen
class TextReadingScreen : PreferenceScreenCreator {
override val key: String
get() = KEY
override val title: Int
get() = R.string.accessibility_text_reading_options_title
override fun isFlagEnabled(context: Context) = Flags.catalystTextReadingScreen()
override fun fragmentClass() = TextReadingPreferenceFragment::class.java
override fun hasCompleteHierarchy() = false
override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
companion object {
const val KEY = "text_reading_screen"
}
}

View File

@@ -20,6 +20,7 @@ import static com.android.settings.accessibility.AccessibilityDialogUtils.Dialog
import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
@@ -53,6 +54,7 @@ import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/** Fragment for providing toggle bar and basic accessibility service setup. */
@@ -323,6 +325,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
}
}
@SuppressLint("MissingPermission")
@Override
public void onToggleClicked(ShortcutPreference preference) {
final int shortcutTypes = getUserPreferredShortcutTypes();
@@ -337,8 +340,10 @@ public class ToggleAccessibilityServicePreferenceFragment extends
onAllowButtonFromShortcutToggleClicked();
}
} else {
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes,
mComponentName);
getPrefContext().getSystemService(AccessibilityManager.class)
.enableShortcutsForTargets(false, shortcutTypes,
Set.of(mComponentName.flattenToString()),
getPrefContext().getUserId());
}
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
@@ -385,8 +390,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
}
// Get Accessibility service name.
mPackageName = getAccessibilityServiceInfo().getResolveInfo().loadLabel(
getPackageManager());
AccessibilityServiceInfo info = getAccessibilityServiceInfo();
mFeatureName = info == null ? "" : info.getResolveInfo().loadLabel(getPackageManager());
if (arguments.containsKey(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME)) {
final String tileServiceComponentName = arguments.getString(
@@ -452,15 +457,11 @@ public class ToggleAccessibilityServicePreferenceFragment extends
@Override
protected int getDefaultShortcutTypes() {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
AccessibilityServiceInfo info = getAccessibilityServiceInfo();
boolean isAccessibilityTool = info != null && info.isAccessibilityTool();
return !isAccessibilityTool || getTileComponentName() == null
? super.getDefaultShortcutTypes()
: ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
}
return super.getDefaultShortcutTypes();
AccessibilityServiceInfo info = getAccessibilityServiceInfo();
boolean isAccessibilityTool = info != null && info.isAccessibilityTool();
return !isAccessibilityTool || getTileComponentName() == null
? super.getDefaultShortcutTypes()
: ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
}
private void onAllowButtonFromEnableToggleClicked() {
@@ -479,11 +480,14 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mWarningDialog.dismiss();
}
@SuppressLint("MissingPermission")
void onAllowButtonFromShortcutToggleClicked() {
mShortcutPreference.setChecked(true);
final int shortcutTypes = getUserPreferredShortcutTypes();
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName);
getPrefContext().getSystemService(AccessibilityManager.class)
.enableShortcutsForTargets(true, shortcutTypes,
Set.of(mComponentName.flattenToString()), getPrefContext().getUserId());
mIsDialogShown.set(false);
showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);

View File

@@ -33,6 +33,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -52,7 +54,10 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
private static final String TAG = "ToggleColorInversionPreferenceFragment";
private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED;
private static final String KEY_SHORTCUT_PREFERENCE = "color_inversion_shortcut_key";
@VisibleForTesting
static final String KEY_SHORTCUT_PREFERENCE = "color_inversion_shortcut_key";
@VisibleForTesting
static final String KEY_SWITCH_PREFERENCE = "color_inversion_switch_preference_key";
@Override
protected void registerKeysToObserverCallback(
@@ -69,13 +74,13 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mComponentName = COLOR_INVERSION_COMPONENT_NAME;
mPackageName = getText(R.string.accessibility_display_inversion_preference_title);
mFeatureName = getText(R.string.accessibility_display_inversion_preference_title);
mHtmlDescription = getText(R.string.accessibility_display_inversion_preference_subtitle);
mTopIntroTitle = getText(R.string.accessibility_display_inversion_preference_intro_text);
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(getPrefContext().getPackageName())
.appendPath(String.valueOf(R.raw.a11y_color_inversion_banner))
.build();
.authority(getPrefContext().getPackageName())
.appendPath(String.valueOf(R.raw.a11y_color_inversion_banner))
.build();
final View view = super.onCreateView(inflater, container, savedInstanceState);
updateFooterPreference();
return view;
@@ -131,6 +136,11 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
switchPreference.setTitle(R.string.accessibility_display_inversion_switch_title);
}
@Override
protected String getUseServicePreferenceKey() {
return KEY_SWITCH_PREFERENCE;
}
@Override
protected CharSequence getShortcutTitle() {
return getText(R.string.accessibility_display_inversion_shortcut_title);
@@ -165,7 +175,7 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
@Override
int getUserShortcutTypes() {
return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
mComponentName);
mComponentName);
}
@Override
@@ -176,8 +186,8 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
@Override
CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
return getText(type == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.string.accessibility_color_inversion_qs_tooltip_content
: R.string.accessibility_color_inversion_auto_added_qs_tooltip_content);
? R.string.accessibility_color_inversion_qs_tooltip_content
: R.string.accessibility_color_inversion_auto_added_qs_tooltip_content);
}
@Override
@@ -194,12 +204,22 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
@Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
boolean enabled) {
final List<SearchIndexableRaw> rawData = new ArrayList<>();
final List<SearchIndexableRaw> rawData =
super.getRawDataToIndex(context, enabled);
SearchIndexableRaw raw = new SearchIndexableRaw(context);
raw.key = KEY_SHORTCUT_PREFERENCE;
raw.title = context.getString(
R.string.accessibility_display_inversion_shortcut_title);
R.string.accessibility_display_inversion_shortcut_title);
rawData.add(raw);
if (Flags.fixA11ySettingsSearch()) {
SearchIndexableRaw mainPreferenceRaw = new SearchIndexableRaw(context);
mainPreferenceRaw.key = KEY_SWITCH_PREFERENCE;
mainPreferenceRaw.title = context.getString(
R.string.accessibility_display_inversion_switch_title);
rawData.add(mainPreferenceRaw);
}
return rawData;
}
};

View File

@@ -25,6 +25,7 @@ import static com.android.settings.accessibility.DaltonizerPreferenceUtil.isSecu
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
@@ -38,6 +39,7 @@ import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltip
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
import java.util.ArrayList;
import java.util.List;
@@ -53,6 +55,11 @@ public class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePreferenceF
private static final String KEY_PROTANOMALY = "daltonizer_mode_protanomaly";
private static final String KEY_TRITANOMEALY = "daltonizer_mode_tritanomaly";
private static final String KEY_GRAYSCALE = "daltonizer_mode_grayscale";
@VisibleForTesting
static final String KEY_SHORTCUT_PREFERENCE = "daltonizer_shortcut_key";
@VisibleForTesting
static final String KEY_SWITCH_PREFERENCE = "daltonizer_switch_preference_key";
@VisibleForTesting
static final String KEY_SATURATION = "daltonizer_saturation";
@@ -71,7 +78,7 @@ public class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePreferenceF
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mComponentName = DALTONIZER_COMPONENT_NAME;
mPackageName = getText(com.android.settingslib.R
mFeatureName = getText(com.android.settingslib.R
.string.accessibility_display_daltonizer_preference_title);
mHtmlDescription = getText(com.android.settingslib.R
.string.accessibility_display_daltonizer_preference_subtitle);
@@ -106,7 +113,7 @@ public class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePreferenceF
final List<String> lists = new ArrayList<>();
lists.add(KEY_TOP_INTRO_PREFERENCE);
lists.add(KEY_PREVIEW);
lists.add(KEY_USE_SERVICE_PREFERENCE);
lists.add(getUseServicePreferenceKey());
// Putting saturation level close to the preview so users can see what is changing.
lists.add(KEY_SATURATION);
lists.add(KEY_DEUTERANOMALY);
@@ -170,6 +177,11 @@ public class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePreferenceF
switchPreference.setTitle(R.string.accessibility_daltonizer_primary_switch_title);
}
@Override
protected String getUseServicePreferenceKey() {
return KEY_SWITCH_PREFERENCE;
}
@Override
protected CharSequence getShortcutTitle() {
return getText(R.string.accessibility_daltonizer_shortcut_title);
@@ -203,5 +215,27 @@ public class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePreferenceF
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_daltonizer_settings);
new BaseSearchIndexProvider(R.xml.accessibility_daltonizer_settings) {
@Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
boolean enabled) {
final List<SearchIndexableRaw> rawData =
super.getRawDataToIndex(context, enabled);
if (Flags.fixA11ySettingsSearch()) {
SearchIndexableRaw shortcutRaw = new SearchIndexableRaw(context);
shortcutRaw.key = KEY_SHORTCUT_PREFERENCE;
shortcutRaw.title = context.getString(
R.string.accessibility_daltonizer_shortcut_title);
rawData.add(shortcutRaw);
SearchIndexableRaw mainSwitchRaw = new SearchIndexableRaw(context);
mainSwitchRaw.key = KEY_SWITCH_PREFERENCE;
mainSwitchRaw.title = context.getString(
R.string.accessibility_daltonizer_primary_switch_title);
rawData.add(mainSwitchRaw);
}
return rawData;
}
};
}

View File

@@ -17,10 +17,9 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
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.Activity;
import android.app.Dialog;
@@ -34,7 +33,6 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
import android.icu.text.CaseMap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -59,14 +57,12 @@ import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.flags.Flags;
import com.android.settings.utils.LocaleUtils;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.widget.IllustrationPreference;
@@ -76,7 +72,7 @@ import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
* Base class for accessibility fragments with toggle, shortcut, some helper functions
@@ -88,7 +84,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
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_USE_SERVICE_PREFERENCE = "use_service";
protected static final String KEY_HTML_DESCRIPTION_PREFERENCE = "html_description";
protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
protected static final String KEY_SAVED_QS_TOOLTIP_TYPE = "qs_tooltip_type";
@@ -110,7 +105,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
protected Intent mSettingsIntent;
// The mComponentName maybe null, such as Magnify
protected ComponentName mComponentName;
protected CharSequence mPackageName;
protected CharSequence mFeatureName;
protected Uri mImageUri;
protected CharSequence mHtmlDescription;
protected CharSequence mTopIntroTitle;
@@ -173,9 +168,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
if (android.view.accessibility.Flags.a11yQsShortcut()) {
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
}
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
return shortcutFeatureKeys;
}
@@ -212,12 +205,12 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
mDialog = AccessibilityShortcutsTutorial
.createAccessibilityTutorialDialogForSetupWizard(
getPrefContext(), getUserPreferredShortcutTypes(),
this::callOnTutorialDialogButtonClicked, mPackageName);
this::callOnTutorialDialogButtonClicked, mFeatureName);
} else {
mDialog = AccessibilityShortcutsTutorial
.createAccessibilityTutorialDialog(
getPrefContext(), getUserPreferredShortcutTypes(),
this::callOnTutorialDialogButtonClicked, mPackageName);
this::callOnTutorialDialogButtonClicked, mFeatureName);
}
mDialog.setCanceledOnTouchOutside(false);
return mDialog;
@@ -326,12 +319,16 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
final CharSequence title =
getString(R.string.accessibility_service_primary_switch_title, mPackageName);
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, mPackageName);
return getString(R.string.accessibility_shortcut_title, mFeatureName);
}
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
@@ -416,7 +413,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
final List<String> lists = new ArrayList<>();
lists.add(KEY_TOP_INTRO_PREFERENCE);
lists.add(KEY_ANIMATED_IMAGE);
lists.add(KEY_USE_SERVICE_PREFERENCE);
lists.add(getUseServicePreferenceKey());
lists.add(KEY_GENERAL_CATEGORY);
lists.add(KEY_HTML_DESCRIPTION_PREFERENCE);
return lists;
@@ -481,7 +478,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
private void initToggleServiceSwitchPreference() {
mToggleServiceSwitchPreference = new SettingsMainSwitchPreference(getPrefContext());
mToggleServiceSwitchPreference.setKey(KEY_USE_SERVICE_PREFERENCE);
mToggleServiceSwitchPreference.setKey(getUseServicePreferenceKey());
if (getArguments().containsKey(AccessibilitySettings.EXTRA_CHECKED)) {
final boolean enabled = getArguments().getBoolean(AccessibilitySettings.EXTRA_CHECKED);
mToggleServiceSwitchPreference.setChecked(enabled);
@@ -577,7 +574,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
screen.addPreference(mHtmlFooterPreference);
// TODO(b/171272809): Migrate to DashboardFragment.
final String title = getString(R.string.accessibility_introduction_title, mPackageName);
final String title = getString(R.string.accessibility_introduction_title, mFeatureName);
mFooterPreferenceController = new AccessibilityFooterPreferenceController(
screen.getContext(), mHtmlFooterPreference.getKey());
mFooterPreferenceController.setIntroductionTitle(title);
@@ -602,7 +599,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
private void initFooterPreference() {
if (!TextUtils.isEmpty(mDescription)) {
createFooterPreference(getPreferenceScreen(), mDescription,
getString(R.string.accessibility_introduction_title, mPackageName));
getString(R.string.accessibility_introduction_title, mFeatureName));
}
}
@@ -627,10 +624,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
mFooterPreferenceController.displayPreference(screen);
}
private boolean hasShortcutType(int value, @UserShortcutType int type) {
return (value & type) == type;
}
protected CharSequence getShortcutTypeSummary(Context context) {
if (!mShortcutPreference.isSettingsEditable()) {
return context.getText(R.string.accessibility_shortcut_edit_dialog_title_hardware);
@@ -640,47 +633,9 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
return context.getText(R.string.accessibility_shortcut_state_off);
}
// LINT.IfChange(shortcut_type_ui_order)
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(
context, mComponentName.flattenToString(), getDefaultShortcutTypes());
final List<CharSequence> list = new ArrayList<>();
if (android.view.accessibility.Flags.a11yQsShortcut()) {
if (hasShortcutType(shortcutTypes, QUICK_SETTINGS)) {
final CharSequence qsTitle = context.getText(
R.string.accessibility_feature_shortcut_setting_summary_quick_settings);
list.add(qsTitle);
}
}
if (hasShortcutType(shortcutTypes, SOFTWARE)) {
list.add(getSoftwareShortcutTypeSummary(context));
}
if (hasShortcutType(shortcutTypes, HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
// LINT.ThenChange(/res/xml/accessibility_edit_shortcuts.xml:shortcut_type_ui_order)
// Show software shortcut if first time to use.
if (list.isEmpty()) {
list.add(getSoftwareShortcutTypeSummary(context));
}
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
null, LocaleUtils.getConcatenatedString(list));
}
private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
} else {
resId = R.string.accessibility_shortcut_edit_summary_software;
}
return context.getText(resId);
return getShortcutSummaryList(context, shortcutTypes);
}
/**
@@ -731,13 +686,12 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
}
final int shortcutTypes = getUserPreferredShortcutTypes();
if (preference.isChecked()) {
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes,
mComponentName);
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);
} else {
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes,
mComponentName);
}
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
@@ -794,44 +748,13 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
showQuickSettingsTooltipIfNeeded();
}
/**
* @deprecated made obsolete by quick settings rollout.
*
* (TODO 367414968: finish removal.)
*/
@Deprecated
private void showQuickSettingsTooltipIfNeeded() {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
// Don't show Quick Settings tooltip
return;
}
final ComponentName tileComponentName = getTileComponentName();
if (tileComponentName == null) {
// Returns if no tile service assigned.
return;
}
Activity activity = getActivity();
if (activity != null && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
// Don't show QuickSettingsTooltip in Setup Wizard
return;
}
if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
getContext(), tileComponentName)) {
// Returns if quick settings tooltip only show once.
return;
}
final CharSequence content = getTileTooltipContent(mNeedsQSTooltipType);
if (TextUtils.isEmpty(content)) {
// Returns if no content of tile tooltip assigned.
return;
}
final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.drawable.accessibility_qs_tooltip_illustration
: R.drawable.accessibility_auto_added_qs_tooltip_illustration;
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
mTooltipWindow.setup(content, imageResId);
mTooltipWindow.showAtTopCenter(getView());
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(getContext(),
tileComponentName);
mNeedsQSTooltipReshow = false;
}
/** Returns user visible name of the tile by given {@link ComponentName}. */

View File

@@ -32,6 +32,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceCategory;
import androidx.preference.TwoStatePreference;
@@ -41,6 +42,7 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SeekBarPreference;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
import java.util.ArrayList;
import java.util.List;
@@ -52,6 +54,10 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
private static final String TAG = "ToggleReduceBrightColorsPreferenceFragment";
private static final String KEY_INTENSITY = "rbc_intensity";
private static final String KEY_PERSIST = "rbc_persist";
@VisibleForTesting
static final String KEY_SHORTCUT = "rbc_shortcut";
@VisibleForTesting
static final String KEY_SWITCH = "rbc_switch";
private static final String REDUCE_BRIGHT_COLORS_ACTIVATED_KEY =
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED;
@@ -78,7 +84,7 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
.appendPath(String.valueOf(R.raw.a11y_extra_dim_banner))
.build();
mComponentName = REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
mPackageName = getText(R.string.reduce_bright_colors_preference_title);
mFeatureName = getText(R.string.reduce_bright_colors_preference_title);
mHtmlDescription = getText(R.string.reduce_bright_colors_preference_subtitle);
mTopIntroTitle = getText(R.string.reduce_bright_colors_preference_intro_text);
mRbcIntensityPreferenceController =
@@ -197,11 +203,43 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
}
}
@Override
protected String getUseServicePreferenceKey() {
return KEY_SWITCH;
}
@Override
protected String getShortcutPreferenceKey() {
return KEY_SHORTCUT;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.reduce_bright_colors_settings) {
@Override
protected boolean isPageSearchEnabled(Context context) {
return ColorDisplayManager.isReduceBrightColorsAvailable(context);
}
@Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
boolean enabled) {
final List<SearchIndexableRaw> rawData =
super.getRawDataToIndex(context, enabled);
if (Flags.fixA11ySettingsSearch()) {
SearchIndexableRaw shortcutRaw = new SearchIndexableRaw(context);
shortcutRaw.key = KEY_SHORTCUT;
shortcutRaw.title = context.getString(
R.string.reduce_bright_colors_shortcut_title);
rawData.add(shortcutRaw);
SearchIndexableRaw mainSwitchRaw = new SearchIndexableRaw(context);
mainSwitchRaw.key = KEY_SWITCH;
mainSwitchRaw.title = context.getString(
R.string.reduce_bright_colors_switch_title);
rawData.add(mainSwitchRaw);
}
return rawData;
}
};
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
@@ -27,6 +28,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
@@ -34,7 +36,6 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.icu.text.CaseMap;
import android.icu.text.MessageFormat;
import android.net.Uri;
import android.os.Bundle;
@@ -53,36 +54,41 @@ import androidx.annotation.StringRes;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.SwitchPreferenceCompat;
import androidx.preference.TwoStatePreference;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.Flags;
import com.android.settings.DialogCreatable;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
import com.android.settings.utils.LocaleUtils;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
import com.android.settingslib.widget.IllustrationPreference;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Stream;
/**
* Fragment that shows the actual UI for providing basic magnification accessibility service setup
* and does not have toggle bar to turn on service to use.
*/
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class ToggleScreenMagnificationPreferenceFragment extends
ToggleFeaturePreferenceFragment implements
MagnificationModePreferenceController.DialogHelper {
private static final String TAG = "ToggleScreenMagnificationPreferenceFragment";
@VisibleForTesting
static final String KEY_MAGNIFICATION_SHORTCUT_PREFERENCE = "magnification_shortcut_preference";
private static final char COMPONENT_NAME_SEPARATOR = ':';
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
@@ -106,7 +112,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mPackageName = getString(R.string.accessibility_screen_magnification_title);
mFeatureName = getString(R.string.accessibility_screen_magnification_title);
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(getPrefContext().getPackageName())
.appendPath(String.valueOf(R.raw.a11y_magnification_banner))
@@ -181,38 +187,29 @@ public class ToggleScreenMagnificationPreferenceFragment extends
}
}
private static boolean isWindowMagnificationSupported(Context context) {
return context.getResources().getBoolean(
com.android.internal.R.bool.config_magnification_area)
&& context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WINDOW_MAGNIFICATION);
}
@Override
protected void initSettingsPreference() {
// If the device doesn't support window magnification feature, it should hide the
// settings preference.
final boolean supportWindowMagnification =
getContext().getResources().getBoolean(
com.android.internal.R.bool.config_magnification_area)
&& getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WINDOW_MAGNIFICATION);
if (!supportWindowMagnification) {
if (!isWindowMagnificationSupported(getContext())) {
return;
}
mSettingsPreference = new Preference(getPrefContext());
mSettingsPreference.setTitle(R.string.accessibility_magnification_mode_title);
mSettingsPreference.setKey(MagnificationModePreferenceController.PREF_KEY);
mSettingsPreference.setPersistent(false);
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mSettingsPreference);
final MagnificationModePreferenceController magnificationModePreferenceController =
new MagnificationModePreferenceController(getContext(),
MagnificationModePreferenceController.PREF_KEY);
magnificationModePreferenceController.setDialogHelper(this);
getSettingsLifecycle().addObserver(magnificationModePreferenceController);
magnificationModePreferenceController.displayPreference(getPreferenceScreen());
addPreferenceController(magnificationModePreferenceController);
// LINT.IfChange(preference_list)
addMagnificationModeSetting(generalCategory);
addFollowTypingSetting(generalCategory);
addOneFingerPanningSetting(generalCategory);
addAlwaysOnSetting(generalCategory);
addJoystickSetting(generalCategory);
// LINT.ThenChange(search_data)
}
@Override
@@ -233,22 +230,44 @@ public class ToggleScreenMagnificationPreferenceFragment extends
&& !Flags.enableMagnificationOneFingerPanningGesture()) {
String summary = MessageFormat.format(
context.getString(R.string.accessibility_screen_magnification_summary),
new Object[]{1, 2, 3, 4, 5});
new Object[]{1, 2, 3, 4, 5});
arguments.putCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, summary);
}
super.onProcessArguments(arguments);
}
private static Preference createMagnificationModePreference(Context context) {
final Preference pref = new Preference(context);
pref.setTitle(R.string.accessibility_magnification_mode_title);
pref.setKey(MagnificationModePreferenceController.PREF_KEY);
pref.setPersistent(false);
return pref;
}
private void addMagnificationModeSetting(PreferenceCategory generalCategory) {
mSettingsPreference = createMagnificationModePreference(getPrefContext());
generalCategory.addPreference(mSettingsPreference);
final MagnificationModePreferenceController magnificationModePreferenceController =
new MagnificationModePreferenceController(getContext(),
MagnificationModePreferenceController.PREF_KEY);
magnificationModePreferenceController.setDialogHelper(this);
getSettingsLifecycle().addObserver(magnificationModePreferenceController);
magnificationModePreferenceController.displayPreference(getPreferenceScreen());
addPreferenceController(magnificationModePreferenceController);
}
private static Preference createFollowTypingPreference(Context context) {
final Preference pref = new SwitchPreferenceCompat(context);
pref.setTitle(R.string.accessibility_screen_magnification_follow_typing_title);
pref.setSummary(R.string.accessibility_screen_magnification_follow_typing_summary);
pref.setKey(MagnificationFollowTypingPreferenceController.PREF_KEY);
return pref;
}
private void addFollowTypingSetting(PreferenceCategory generalCategory) {
var followingTypingSwitchPreference = new SwitchPreferenceCompat(getPrefContext());
followingTypingSwitchPreference.setTitle(
R.string.accessibility_screen_magnification_follow_typing_title);
followingTypingSwitchPreference.setSummary(
R.string.accessibility_screen_magnification_follow_typing_summary);
followingTypingSwitchPreference.setKey(
MagnificationFollowTypingPreferenceController.PREF_KEY);
generalCategory.addPreference(followingTypingSwitchPreference);
generalCategory.addPreference(createFollowTypingPreference(getPrefContext()));
var followTypingPreferenceController = new MagnificationFollowTypingPreferenceController(
getContext(), MagnificationFollowTypingPreferenceController.PREF_KEY);
@@ -257,8 +276,8 @@ public class ToggleScreenMagnificationPreferenceFragment extends
addPreferenceController(followTypingPreferenceController);
}
private boolean isAlwaysOnSettingEnabled() {
final boolean defaultValue = getContext().getResources().getBoolean(
private static boolean isAlwaysOnSupported(Context context) {
final boolean defaultValue = context.getResources().getBoolean(
com.android.internal.R.bool.config_magnification_always_on_enabled);
return DeviceConfig.getBoolean(
@@ -268,19 +287,21 @@ public class ToggleScreenMagnificationPreferenceFragment extends
);
}
private static Preference createAlwaysOnPreference(Context context) {
final Preference pref = new SwitchPreferenceCompat(context);
pref.setTitle(R.string.accessibility_screen_magnification_always_on_title);
pref.setSummary(R.string.accessibility_screen_magnification_always_on_summary);
pref.setKey(MagnificationAlwaysOnPreferenceController.PREF_KEY);
return pref;
}
private void addAlwaysOnSetting(PreferenceCategory generalCategory) {
if (!isAlwaysOnSettingEnabled()) {
if (!isAlwaysOnSupported(getContext())) {
return;
}
var alwaysOnPreference = new SwitchPreferenceCompat(getPrefContext());
alwaysOnPreference.setTitle(
R.string.accessibility_screen_magnification_always_on_title);
alwaysOnPreference.setSummary(
R.string.accessibility_screen_magnification_always_on_summary);
alwaysOnPreference.setKey(
MagnificationAlwaysOnPreferenceController.PREF_KEY);
generalCategory.addPreference(alwaysOnPreference);
final Preference pref = createAlwaysOnPreference(getPrefContext());
generalCategory.addPreference(pref);
var alwaysOnPreferenceController = new MagnificationAlwaysOnPreferenceController(
getContext(), MagnificationAlwaysOnPreferenceController.PREF_KEY);
@@ -290,17 +311,24 @@ public class ToggleScreenMagnificationPreferenceFragment extends
addPreferenceController(alwaysOnPreferenceController);
}
private static Preference createOneFingerPanningPreference(Context context) {
final Preference pref = new SwitchPreferenceCompat(context);
pref.setTitle(R.string.accessibility_magnification_one_finger_panning_title);
pref.setKey(MagnificationOneFingerPanningPreferenceController.PREF_KEY);
return pref;
}
private static boolean isOneFingerPanningSupported() {
return Flags.enableMagnificationOneFingerPanningGesture();
}
private void addOneFingerPanningSetting(PreferenceCategory generalCategory) {
if (!Flags.enableMagnificationOneFingerPanningGesture()) {
if (!isOneFingerPanningSupported()) {
return;
}
var oneFingerPanningPreference = new SwitchPreferenceCompat(getPrefContext());
oneFingerPanningPreference.setTitle(
R.string.accessibility_magnification_one_finger_panning_title);
oneFingerPanningPreference.setKey(
MagnificationOneFingerPanningPreferenceController.PREF_KEY);
generalCategory.addPreference(oneFingerPanningPreference);
final Preference pref = createOneFingerPanningPreference(getPrefContext());
generalCategory.addPreference(pref);
mOneFingerPanningPreferenceController =
new MagnificationOneFingerPanningPreferenceController(getContext());
@@ -310,23 +338,28 @@ public class ToggleScreenMagnificationPreferenceFragment extends
addPreferenceController(mOneFingerPanningPreferenceController);
}
private void addJoystickSetting(PreferenceCategory generalCategory) {
if (!DeviceConfig.getBoolean(
private static Preference createJoystickPreference(Context context) {
final Preference pref = new SwitchPreferenceCompat(context);
pref.setTitle(R.string.accessibility_screen_magnification_joystick_title);
pref.setSummary(R.string.accessibility_screen_magnification_joystick_summary);
pref.setKey(MagnificationJoystickPreferenceController.PREF_KEY);
return pref;
}
private static boolean isJoystickSupported() {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
"MagnificationJoystick__enable_magnification_joystick",
false
)) {
false);
}
private void addJoystickSetting(PreferenceCategory generalCategory) {
if (!isJoystickSupported()) {
return;
}
TwoStatePreference joystickPreference = new SwitchPreferenceCompat(getPrefContext());
joystickPreference.setTitle(
R.string.accessibility_screen_magnification_joystick_title);
joystickPreference.setSummary(
R.string.accessibility_screen_magnification_joystick_summary);
joystickPreference.setKey(
MagnificationJoystickPreferenceController.PREF_KEY);
generalCategory.addPreference(joystickPreference);
final Preference pref = createJoystickPreference(getPrefContext());
generalCategory.addPreference(pref);
MagnificationJoystickPreferenceController joystickPreferenceController =
new MagnificationJoystickPreferenceController(
@@ -348,31 +381,15 @@ public class ToggleScreenMagnificationPreferenceFragment extends
mDialogDelegate = delegate;
}
private boolean hasShortcutType(int value, @UserShortcutType int type) {
return (value & type) == type;
}
private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
} else {
resId = R.string.accessibility_shortcut_edit_summary_software;
}
return context.getText(resId);
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
var keysToObserve = List.of(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED
);
contentObserver.registerKeysToObserverCallback(keysToObserve,
key -> updatePreferencesState());
@@ -423,47 +440,9 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return context.getText(R.string.switch_off_text);
}
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
MAGNIFICATION_CONTROLLER_NAME);
// LINT.IfChange(shortcut_type_ui_order)
final List<CharSequence> list = new ArrayList<>();
if (android.view.accessibility.Flags.a11yQsShortcut()) {
if (hasShortcutType(shortcutTypes, QUICK_SETTINGS)) {
final CharSequence qsTitle = context.getText(
R.string.accessibility_feature_shortcut_setting_summary_quick_settings);
list.add(qsTitle);
}
}
if (hasShortcutType(shortcutTypes, SOFTWARE)) {
list.add(getSoftwareShortcutTypeSummary(context));
}
if (hasShortcutType(shortcutTypes, HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if (hasShortcutType(shortcutTypes, TWOFINGER_DOUBLETAP)) {
final CharSequence twoFingerDoubleTapTitle = context.getString(
R.string.accessibility_shortcut_two_finger_double_tap_keyword, 2);
list.add(twoFingerDoubleTapTitle);
}
}
if (hasShortcutType(shortcutTypes, TRIPLETAP)) {
final CharSequence tripleTapTitle = context.getText(
R.string.accessibility_shortcut_triple_tap_keyword);
list.add(tripleTapTitle);
}
// LINT.ThenChange(/res/xml/accessibility_edit_shortcuts.xml:shortcut_type_ui_order)
// Show software shortcut if first time to use.
if (list.isEmpty()) {
list.add(getSoftwareShortcutTypeSummary(context));
}
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
null, LocaleUtils.getConcatenatedString(list));
return getShortcutSummaryList(context,
PreferredShortcuts.retrieveUserShortcutType(context,
MAGNIFICATION_CONTROLLER_NAME));
}
@Override
@@ -571,6 +550,11 @@ public class ToggleScreenMagnificationPreferenceFragment extends
generalCategory.addPreference(mShortcutPreference);
}
@Override
protected String getShortcutPreferenceKey() {
return KEY_MAGNIFICATION_SHORTCUT_PREFERENCE;
}
@Override
protected CharSequence getShortcutTitle() {
return getText(R.string.accessibility_screen_magnification_shortcut_title);
@@ -595,76 +579,38 @@ public class ToggleScreenMagnificationPreferenceFragment extends
if (((shortcutTypes & TRIPLETAP) == TRIPLETAP)) {
optInMagnificationValueToSettings(context, TRIPLETAP);
}
if (((shortcutTypes & GESTURE) == GESTURE)) {
optInMagnificationValueToSettings(context, GESTURE);
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if (((shortcutTypes & TWOFINGER_DOUBLETAP)
== TWOFINGER_DOUBLETAP)) {
optInMagnificationValueToSettings(context, TWOFINGER_DOUBLETAP);
}
}
if (android.view.accessibility.Flags.a11yQsShortcut()) {
if (((shortcutTypes & QUICK_SETTINGS)
== QUICK_SETTINGS)) {
optInMagnificationValueToSettings(context, QUICK_SETTINGS);
}
if (((shortcutTypes & QUICK_SETTINGS)
== QUICK_SETTINGS)) {
optInMagnificationValueToSettings(context, QUICK_SETTINGS);
}
}
/**
* @deprecated use
* {@link AccessibilityManager#enableShortcutsForTargets(boolean, int, Set, int)} instead.
*
* (TODO 367414968: finish removal.)
*/
@Deprecated
private static void optInMagnificationValueToSettings(
Context context, @UserShortcutType int shortcutType) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
AccessibilityManager a11yManager = context.getSystemService(AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(
/* enable= */ true,
shortcutType,
Set.of(MAGNIFICATION_CONTROLLER_NAME),
UserHandle.myUserId()
);
}
return;
}
if (shortcutType == TRIPLETAP) {
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, ON);
return;
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if (shortcutType == TWOFINGER_DOUBLETAP) {
Settings.Secure.putInt(
context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
ON);
return;
}
}
if (hasMagnificationValueInSettings(context, shortcutType)) {
return;
}
final String targetKey = AccessibilityUtil.convertKeyFromSettings(shortcutType);
final String targetString = Settings.Secure.getString(context.getContentResolver(),
targetKey);
final StringJoiner joiner = new StringJoiner(String.valueOf(COMPONENT_NAME_SEPARATOR));
if (!TextUtils.isEmpty(targetString)) {
joiner.add(targetString);
}
joiner.add(MAGNIFICATION_CONTROLLER_NAME);
Settings.Secure.putString(context.getContentResolver(), targetKey, joiner.toString());
// The size setting defaults to unknown. If the user has ever manually changed the size
// before, we do not automatically change it.
if (shortcutType == SOFTWARE
&& Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
FloatingMenuSizePreferenceController.Size.UNKNOWN)
== FloatingMenuSizePreferenceController.Size.UNKNOWN) {
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
FloatingMenuSizePreferenceController.Size.LARGE);
AccessibilityManager a11yManager = context.getSystemService(AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(
/* enable= */ true,
shortcutType,
Set.of(MAGNIFICATION_CONTROLLER_NAME),
UserHandle.myUserId()
);
}
}
@@ -680,94 +626,58 @@ public class ToggleScreenMagnificationPreferenceFragment extends
if (((shortcutTypes & TRIPLETAP) == TRIPLETAP)) {
optOutMagnificationValueFromSettings(context, TRIPLETAP);
}
if (((shortcutTypes & GESTURE) == GESTURE)) {
optOutMagnificationValueFromSettings(context, GESTURE);
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if (((shortcutTypes & TWOFINGER_DOUBLETAP)
== TWOFINGER_DOUBLETAP)) {
optOutMagnificationValueFromSettings(context, TWOFINGER_DOUBLETAP);
}
}
if (android.view.accessibility.Flags.a11yQsShortcut()) {
if (((shortcutTypes & QUICK_SETTINGS)
if (((shortcutTypes & QUICK_SETTINGS)
== QUICK_SETTINGS)) {
optOutMagnificationValueFromSettings(context, QUICK_SETTINGS);
}
optOutMagnificationValueFromSettings(context, QUICK_SETTINGS);
}
}
/**
* @deprecated use
* {@link AccessibilityManager#enableShortcutsForTargets(boolean, int, Set, int)} instead.
*
* (TODO 367414968: finish removal.)
*/
@Deprecated
private static void optOutMagnificationValueFromSettings(Context context,
@UserShortcutType int shortcutType) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
AccessibilityManager a11yManager = context.getSystemService(AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(
/* enable= */ false,
shortcutType,
Set.of(MAGNIFICATION_CONTROLLER_NAME),
UserHandle.myUserId()
);
}
return;
AccessibilityManager a11yManager = context.getSystemService(AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(
/* enable= */ false,
shortcutType,
Set.of(MAGNIFICATION_CONTROLLER_NAME),
UserHandle.myUserId()
);
}
if (shortcutType == TRIPLETAP) {
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF);
return;
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if (shortcutType == TWOFINGER_DOUBLETAP) {
Settings.Secure.putInt(
context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
OFF);
return;
}
}
final String targetKey = AccessibilityUtil.convertKeyFromSettings(shortcutType);
final String targetString = Settings.Secure.getString(context.getContentResolver(),
targetKey);
if (TextUtils.isEmpty(targetString)) {
return;
}
final StringJoiner joiner = new StringJoiner(String.valueOf(COMPONENT_NAME_SEPARATOR));
sStringColonSplitter.setString(targetString);
while (sStringColonSplitter.hasNext()) {
final String name = sStringColonSplitter.next();
if (TextUtils.isEmpty(name) || MAGNIFICATION_CONTROLLER_NAME.equals(name)) {
continue;
}
joiner.add(name);
}
Settings.Secure.putString(context.getContentResolver(), targetKey, joiner.toString());
}
@VisibleForTesting
static boolean hasMagnificationValuesInSettings(Context context, int shortcutTypes) {
boolean exist = false;
if ((shortcutTypes & SOFTWARE) == SOFTWARE) {
exist = hasMagnificationValueInSettings(context, SOFTWARE);
}
if (((shortcutTypes & HARDWARE) == HARDWARE)) {
exist |= hasMagnificationValueInSettings(context, HARDWARE);
}
if (((shortcutTypes & TRIPLETAP) == TRIPLETAP)) {
exist |= hasMagnificationValueInSettings(context, TRIPLETAP);
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if (((shortcutTypes & TWOFINGER_DOUBLETAP)
== TWOFINGER_DOUBLETAP)) {
exist |= hasMagnificationValueInSettings(context,
TWOFINGER_DOUBLETAP);
for (int shortcutType : AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
if ((shortcutTypes & shortcutType) == 0) {
continue;
}
if (((shortcutType & TWOFINGER_DOUBLETAP)
== TWOFINGER_DOUBLETAP)
&& !Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
continue;
}
if (hasMagnificationValueInSettings(context, shortcutType)) {
return true;
}
}
return exist;
return false;
}
private static boolean hasMagnificationValueInSettings(Context context,
@@ -776,7 +686,6 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF) == ON;
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if (shortcutType == TWOFINGER_DOUBLETAP) {
return Settings.Secure.getInt(context.getContentResolver(),
@@ -803,22 +712,21 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return false;
}
/**
* @deprecated use
* {@link ShortcutUtils#getEnabledShortcutTypes(Context, String)} instead.
*
* (TODO 367414968: finish removal.)
*/
@Deprecated
private static int getUserShortcutTypeFromSettings(Context context) {
int shortcutTypes = DEFAULT;
if (hasMagnificationValuesInSettings(context, SOFTWARE)) {
shortcutTypes |= SOFTWARE;
}
if (hasMagnificationValuesInSettings(context, HARDWARE)) {
shortcutTypes |= HARDWARE;
}
if (hasMagnificationValuesInSettings(context, TRIPLETAP)) {
shortcutTypes |= TRIPLETAP;
}
if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
if (hasMagnificationValuesInSettings(context, TWOFINGER_DOUBLETAP)) {
shortcutTypes |= TWOFINGER_DOUBLETAP;
for (int shortcutType : AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
if (hasMagnificationValueInSettings(context, shortcutType)) {
shortcutTypes |= shortcutType;
}
}
return shortcutTypes;
}
@@ -832,8 +740,8 @@ public class ToggleScreenMagnificationPreferenceFragment extends
final int userShortcutType = getUserShortcutTypeFromSettings(context);
final CharSequence featureState =
(userShortcutType != DEFAULT)
? context.getText(R.string.accessibility_summary_shortcut_enabled)
: context.getText(R.string.generic_accessibility_feature_shortcut_off);
? context.getText(R.string.accessibility_summary_shortcut_enabled)
: context.getText(R.string.generic_accessibility_feature_shortcut_off);
final CharSequence featureSummary = context.getText(R.string.magnification_feature_summary);
return context.getString(
com.android.settingslib.R.string.preference_summary_default_combination,
@@ -845,4 +753,81 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return PreferredShortcuts.retrieveUserShortcutType(
getPrefContext(), MAGNIFICATION_CONTROLLER_NAME);
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
// LINT.IfChange(search_data)
@Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
boolean enabled) {
final List<SearchIndexableRaw> rawData =
super.getRawDataToIndex(context, enabled);
if (!com.android.settings.accessibility.Flags.fixA11ySettingsSearch()) {
return rawData;
}
rawData.add(createShortcutPreferenceSearchData(context));
Stream.of(
createMagnificationModePreference(context),
createFollowTypingPreference(context),
createOneFingerPanningPreference(context),
createAlwaysOnPreference(context),
createJoystickPreference(context)
)
.forEach(pref ->
rawData.add(createPreferenceSearchData(context, pref)));
return rawData;
}
@Override
public List<String> getNonIndexableKeys(Context context) {
final List<String> niks = super.getNonIndexableKeys(context);
if (!com.android.settings.accessibility.Flags.fixA11ySettingsSearch()) {
return niks;
}
if (!isWindowMagnificationSupported(context)) {
niks.add(MagnificationModePreferenceController.PREF_KEY);
niks.add(MagnificationFollowTypingPreferenceController.PREF_KEY);
niks.add(MagnificationOneFingerPanningPreferenceController.PREF_KEY);
niks.add(MagnificationAlwaysOnPreferenceController.PREF_KEY);
niks.add(MagnificationJoystickPreferenceController.PREF_KEY);
} else {
if (!isAlwaysOnSupported(context)
// This preference's title "Keep on while switching apps" does not
// mention magnification so it may confuse users who search a term
// like "Keep on".
// So we hide it if the user has no magnification shortcut enabled.
|| getUserShortcutTypeFromSettings(context) == DEFAULT) {
niks.add(MagnificationAlwaysOnPreferenceController.PREF_KEY);
}
if (!isOneFingerPanningSupported()) {
niks.add(MagnificationOneFingerPanningPreferenceController.PREF_KEY);
}
if (!isJoystickSupported()) {
niks.add(MagnificationJoystickPreferenceController.PREF_KEY);
}
}
return niks;
}
// LINT.ThenChange(preference_list)
private SearchIndexableRaw createPreferenceSearchData(
Context context, Preference pref) {
final SearchIndexableRaw raw = new SearchIndexableRaw(context);
raw.key = pref.getKey();
raw.title = pref.getTitle().toString();
return raw;
}
private SearchIndexableRaw createShortcutPreferenceSearchData(Context context) {
final SearchIndexableRaw raw = new SearchIndexableRaw(context);
raw.key = KEY_MAGNIFICATION_SHORTCUT_PREFERENCE;
raw.title = context.getString(
R.string.accessibility_screen_magnification_shortcut_title);
return raw;
}
};
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility
import android.content.Context
import androidx.fragment.app.Fragment
import com.android.settings.R
import com.android.settings.flags.Flags
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
/**
* Accessibility settings for vibration intensities.
*/
// TODO(b/368360218): investigate if we still need this screen once we finish the migration.
// We might be able to consolidate this into VibrationScreen with PreferenceHierarchy choosing
// between toggle or slider preferences based on device config, depending on how overlays are done.
// LINT.IfChange
@ProvidePreferenceScreen
class VibrationIntensityScreen : PreferenceScreenCreator, PreferenceAvailabilityProvider {
override val key: String
get() = KEY
override val title: Int
get() = R.string.accessibility_vibration_settings_title
override val keywords: Int
get() = R.string.keywords_vibration
override fun isAvailable(context: Context) =
context.isVibratorAvailable() && context.getSupportedVibrationIntensityLevels() > 1
override fun isFlagEnabled(context: Context): Boolean = Flags.catalystVibrationIntensityScreen()
override fun hasCompleteHierarchy() = false
override fun fragmentClass(): Class<out Fragment>? =
VibrationIntensitySettingsFragment::class.java
override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {
+VibrationMainSwitchPreference()
}
companion object {
const val KEY = "vibration_intensity_screen"
}
}
// LINT.ThenChange(VibrationPreferenceController.java)

View File

@@ -25,6 +25,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.RecyclerView;
@@ -90,4 +92,9 @@ public class VibrationIntensitySettingsFragment extends DashboardFragment {
return VibrationIntensitySettingsFragment.isPageSearchEnabled(context);
}
};
@Override
public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
return VibrationIntensityScreen.KEY;
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2024 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.os.VibrationAttributes
import android.os.Vibrator
import android.provider.Settings
import android.widget.CompoundButton
import android.widget.CompoundButton.OnCheckedChangeListener
import com.android.settings.R
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObservableDelegate
import com.android.settingslib.datastore.SettingsStore
import com.android.settingslib.datastore.SettingsSystemStore
import com.android.settingslib.metadata.MainSwitchPreference
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.preference.MainSwitchPreferenceBinding
/**
* Accessibility settings for vibration.
*/
// LINT.IfChange
class VibrationMainSwitchPreference : MainSwitchPreference(
key = Settings.System.VIBRATE_ON,
title = R.string.accessibility_vibration_primary_switch_title,
), PreferenceLifecycleProvider, OnCheckedChangeListener {
override val keywords: Int
get() = R.string.keywords_accessibility_vibration_primary_switch
lateinit var vibrator: Vibrator
override fun storage(context: Context): KeyValueStore =
VibrationMainSwitchToggleStorage(SettingsSystemStore.get(context))
override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
override fun onResume(context: PreferenceLifecycleContext) {
vibrator = context.getSystemService(Vibrator::class.java)
context.findPreference<com.android.settingslib.widget.MainSwitchPreference>(key)
?.addOnSwitchChangeListener(this)
}
override fun onPause(context: PreferenceLifecycleContext) {
context.findPreference<com.android.settingslib.widget.MainSwitchPreference>(key)
?.removeOnSwitchChangeListener(this)
}
override fun onCheckedChanged(button: CompoundButton, isChecked: Boolean) {
if (isChecked) {
// Play a haptic as preview for the main toggle only when touch feedback is enabled.
VibrationPreferenceConfig.playVibrationPreview(
vibrator, VibrationAttributes.USAGE_TOUCH
)
}
}
/** Provides SettingsStore for vibration main switch with custom default value. */
@Suppress("UNCHECKED_CAST")
private class VibrationMainSwitchToggleStorage(
private val settingsStore: SettingsStore,
) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
override fun contains(key: String) = settingsStore.contains(key)
override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
DEFAULT_VALUE as T
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
(settingsStore.getBoolean(key) ?: DEFAULT_VALUE) as T
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
settingsStore.setBoolean(key, value as Boolean?)
}
}
companion object {
const val DEFAULT_VALUE = true
}
}
// LINT.ThenChange(VibrationMainSwitchPreferenceController.java)

View File

@@ -41,6 +41,7 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
* will disable the entire settings screen once the settings is turned OFF. All device haptics will
* be disabled by this setting, except the flagged alerts and accessibility touch feedback.
*/
// LINT.IfChange
public class VibrationMainSwitchPreferenceController extends SettingsMainSwitchPreferenceController
implements LifecycleObserver, OnStart, OnStop {
@@ -106,3 +107,4 @@ public class VibrationMainSwitchPreferenceController extends SettingsMainSwitchP
return R.string.menu_key_accessibility;
}
}
// LINT.ThenChange(VibrationMainSwitchPreference.kt)

View File

@@ -31,6 +31,7 @@ import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
/** Controller for "Vibration & haptics" settings page. */
// LINT.IfChange
public class VibrationPreferenceController extends BasePreferenceController {
private final boolean mHasVibrator;
@@ -79,3 +80,7 @@ public class VibrationPreferenceController extends BasePreferenceController {
}
// LINT.ThenChange(
// VibrationIntensityScreenTest.kt,
// VibrationScreenTest.kt,
// )

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2024 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.os.Vibrator
import androidx.fragment.app.Fragment
import com.android.settings.R
import com.android.settings.flags.Flags
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
/**
* Accessibility settings for vibration.
*/
// LINT.IfChange
@ProvidePreferenceScreen
class VibrationScreen : PreferenceScreenCreator, PreferenceAvailabilityProvider {
override val key: String
get() = KEY
override val title: Int
get() = R.string.accessibility_vibration_settings_title
override val keywords: Int
get() = R.string.keywords_vibration
override fun isAvailable(context: Context) =
context.isVibratorAvailable() && context.getSupportedVibrationIntensityLevels() == 1
override fun isFlagEnabled(context: Context): Boolean = Flags.catalystVibrationIntensityScreen()
override fun hasCompleteHierarchy() = false
override fun fragmentClass(): Class<out Fragment>? = VibrationSettings::class.java
override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {
+VibrationMainSwitchPreference()
}
companion object {
const val KEY = "vibration_screen"
}
}
/** Returns true if the device has a system vibrator, false otherwise. */
fun Context.isVibratorAvailable(): Boolean =
getSystemService(Vibrator::class.java).hasVibrator()
/** Returns the number of vibration intensity levels supported by this device. */
fun Context.getSupportedVibrationIntensityLevels(): Int =
resources.getInteger(R.integer.config_vibration_supported_intensity_levels)
// LINT.ThenChange(VibrationPreferenceController.java)

View File

@@ -20,6 +20,8 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Vibrator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
@@ -35,6 +37,11 @@ public class VibrationSettings extends DashboardFragment {
private static final String TAG = "VibrationSettings";
@Override
public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
return VibrationScreen.KEY;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_VIBRATION;

View File

@@ -55,8 +55,8 @@ public class VolumeShortcutToggleAccessibilityServicePreferenceFragment extends
final boolean isServiceOn =
getArguments().getBoolean(AccessibilitySettings.EXTRA_CHECKED);
final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
final boolean hasRequestAccessibilityButtonFlag =
(info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
final boolean hasRequestAccessibilityButtonFlag = info != null
&& (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
if (hasRequestAccessibilityButtonFlag && isServiceOn) {
shortcutTypes |= SOFTWARE;
} else {

View File

@@ -61,6 +61,7 @@ import com.android.internal.accessibility.dialog.AccessibilityTargetHelper;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.accessibility.AccessibilitySetupWizardUtils;
import com.android.settings.accessibility.Flags;
import com.android.settings.accessibility.PreferredShortcuts;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
@@ -201,9 +202,14 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment {
super.onCreatePreferences(savedInstanceState, rootKey);
Activity activity = getActivity();
final Preference descriptionPref = findPreference(getString(
R.string.accessibility_shortcut_description_pref));
if (!activity.getIntent().getAction().equals(
Settings.ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS)) {
if (Flags.toggleFeatureFragmentCollectionInfo()) {
descriptionPref.setVisible(false);
}
return;
}
@@ -219,10 +225,11 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment {
);
activity.setTitle(titles.first);
String screenDescriptionPrefKey = getString(
R.string.accessibility_shortcut_description_pref);
findPreference(screenDescriptionPrefKey).setSummary(titles.second);
if (titles.second != null || !Flags.toggleFeatureFragmentCollectionInfo()) {
descriptionPref.setSummary(titles.second);
} else {
descriptionPref.setVisible(false);
}
}
@NonNull

View File

@@ -25,7 +25,6 @@ import android.os.UserHandle;
import android.service.quicksettings.TileService;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
@@ -82,8 +81,7 @@ public class QuickSettingsShortcutOptionController extends ShortcutOptionPrefere
@Override
protected boolean isShortcutAvailable() {
return Flags.a11yQsShortcut()
&& TileService.isQuickSettingsSupported()
return TileService.isQuickSettingsSupported()
&& allTargetsHasQsTile()
&& allTargetsHasValidQsTileUseCase();
}

View File

@@ -35,7 +35,6 @@ import com.android.settings.R;
import com.android.settingslib.widget.LottieColorUtils;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
/**
* A preference represents an accessibility shortcut option with a checkbox and a tutorial image
@@ -96,7 +95,8 @@ public class ShortcutOptionPreference extends CheckBoxPreference {
.getResourceEntryName(mIntroImageRawResId),
result));
imageView.setAnimation(mIntroImageRawResId);
imageView.setRepeatCount(LottieDrawable.INFINITE);
// Follow the Motion Stoppable requirement by using a finite animation.
imageView.setRepeatCount(0);
LottieColorUtils.applyDynamicColors(imageView.getContext(), imageView);
imageView.playAnimation();
} else {

View File

@@ -19,7 +19,6 @@ package com.android.settings.accessibility.shortcuts;
import android.content.Context;
import android.os.UserHandle;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
@@ -111,36 +110,27 @@ public abstract class ShortcutOptionPreferenceController extends BasePreferenceC
return !targets.isEmpty() && targets.containsAll(getShortcutTargets());
}
/**
* Enable or disable the shortcut for the given accessibility features.
*
* @deprecated use
* {@link AccessibilityManager#enableShortcutsForTargets(boolean, int, Set, int)} instead.
*
* (TODO 367414968: finish removal.)
*/
@Deprecated
protected void enableShortcutForTargets(boolean enable) {
Set<String> shortcutTargets = getShortcutTargets();
@ShortcutConstants.UserShortcutType int shortcutType = getShortcutType();
if (Flags.a11yQsShortcut()) {
AccessibilityManager a11yManager = mContext.getSystemService(
AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(enable, shortcutType, shortcutTargets,
UserHandle.myUserId());
}
return;
AccessibilityManager a11yManager = mContext.getSystemService(
AccessibilityManager.class);
if (a11yManager != null) {
a11yManager.enableShortcutsForTargets(enable, shortcutType, shortcutTargets,
UserHandle.myUserId());
}
if (enable) {
for (String target : shortcutTargets) {
ShortcutUtils.optInValueToSettings(mContext, shortcutType, target);
}
} else {
for (String target : shortcutTargets) {
ShortcutUtils.optOutValueFromSettings(mContext, shortcutType, target);
}
}
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
mContext, shortcutTargets, UserHandle.myUserId());
}
/**
* Returns true when the user can associate a shortcut to the targets
*/

View File

@@ -19,14 +19,11 @@ package com.android.settings.accessibility.shortcuts;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import android.content.Context;
import android.provider.Settings;
import android.view.View;
import android.view.accessibility.Flags;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityButtonFragment;
import com.android.settings.accessibility.FloatingMenuSizePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.utils.AnnotationSpan;
@@ -62,26 +59,4 @@ public abstract class SoftwareShortcutOptionPreferenceController
R.string.accessibility_shortcut_edit_dialog_summary_software_floating),
linkInfo);
}
@Override
protected void enableShortcutForTargets(boolean enable) {
super.enableShortcutForTargets(enable);
if (Flags.a11yQsShortcut()) {
return;
}
if (enable) {
// Update the A11y FAB size to large when the Magnification shortcut is enabled
// and the user hasn't changed the floating button size
if (isMagnificationInTargets()
&& Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
FloatingMenuSizePreferenceController.Size.UNKNOWN)
== FloatingMenuSizePreferenceController.Size.UNKNOWN) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
FloatingMenuSizePreferenceController.Size.LARGE);
}
}
}
}

View File

@@ -20,7 +20,6 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import android.content.Context;
import android.provider.Settings;
import android.view.accessibility.Flags;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -99,17 +98,4 @@ public class TripleTapShortcutOptionController extends ShortcutOptionPreferenceC
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
AccessibilityUtil.State.OFF) == AccessibilityUtil.State.ON;
}
@Override
protected void enableShortcutForTargets(boolean enable) {
if (Flags.a11yQsShortcut()) {
super.enableShortcutForTargets(enable);
return;
}
Settings.Secure.putInt(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
enable ? AccessibilityUtil.State.ON : AccessibilityUtil.State.OFF);
}
}

View File

@@ -86,16 +86,4 @@ public class TwoFingerDoubleTapShortcutOptionController
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
AccessibilityUtil.State.OFF) == AccessibilityUtil.State.ON;
}
@Override
protected void enableShortcutForTargets(boolean enable) {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
super.enableShortcutForTargets(enable);
return;
}
Settings.Secure.putInt(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
enable ? AccessibilityUtil.State.ON : AccessibilityUtil.State.OFF);
}
}

View File

@@ -17,14 +17,12 @@
package com.android.settings.accessibility.shortcuts;
import android.content.Context;
import android.view.accessibility.Flags;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil;
/**
* A controller handles displaying the volume keys shortcut option preference and
@@ -61,16 +59,4 @@ public class VolumeKeysShortcutOptionController extends ShortcutOptionPreference
protected boolean isShortcutAvailable() {
return true;
}
@Override
protected void enableShortcutForTargets(boolean enable) {
super.enableShortcutForTargets(enable);
if (Flags.a11yQsShortcut()) {
return;
}
if (enable) {
AccessibilityUtil.skipVolumeShortcutDialogTimeoutRestriction(mContext);
}
}
}

View File

@@ -20,6 +20,7 @@ package com.android.settings.accounts;
import android.accounts.Account;
import android.accounts.AuthenticatorDescription;
import android.content.ClipData;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -186,9 +187,9 @@ public class AccountTypePreferenceLoader {
prefIntent, mUserHandle);
} else {
Log.e(TAG,
"Refusing to launch authenticator intent because"
+ "it exploits Settings permissions: "
+ prefIntent);
"Refusing to launch authenticator intent because "
+ "it exploits Settings permissions: "
+ prefIntent);
}
return true;
}
@@ -242,13 +243,19 @@ public class AccountTypePreferenceLoader {
}
/**
* Determines if the supplied Intent is safe. A safe intent is one that is
* will launch a exported=true activity or owned by the same uid as the
* Determines if the supplied Intent is safe. A safe intent is one that
* will launch an exported=true activity or owned by the same uid as the
* authenticator supplying the intent.
*/
private boolean isSafeIntent(PackageManager pm, Intent intent, String acccountType) {
@VisibleForTesting
boolean isSafeIntent(PackageManager pm, Intent intent, String accountType) {
if (TextUtils.equals(intent.getScheme(), ContentResolver.SCHEME_CONTENT)) {
Log.e(TAG, "Intent with a content scheme is unsafe.");
return false;
}
AuthenticatorDescription authDesc =
mAuthenticatorHelper.getAccountTypeDescription(acccountType);
mAuthenticatorHelper.getAccountTypeDescription(accountType);
ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mUserHandle.getIdentifier());
if (resolveInfo == null) {
return false;

View File

@@ -257,9 +257,10 @@ public class ActivityEmbeddingRulesController {
final FingerprintEnrollActivityClassProvider fpClassProvider = FeatureFactory
.getFeatureFactory()
.getFingerprintFeatureProvider()
.getEnrollActivityClassProvider();
.getEnrollActivityClassProvider(mContext);
addActivityFilter(activityFilters, fpClassProvider.getDefault());
addActivityFilter(activityFilters, fpClassProvider.getInternal());
addActivityFilter(activityFilters, fpClassProvider.getAddAnother());
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
addActivityFilter(activityFilters, FaceEnrollIntroduction.class);

View File

@@ -16,8 +16,6 @@
package com.android.settings.applications;
import static android.webkit.Flags.updateServiceV2;
import android.Manifest;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -173,11 +171,9 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide
}
// Keep WebView default package enabled.
if (updateServiceV2()) {
String packageName = mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName();
if (packageName != null) {
keepEnabledPackages.add(packageName);
}
String packageName = mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName();
if (packageName != null) {
keepEnabledPackages.add(packageName);
}
keepEnabledPackages.addAll(getEnabledPackageAllowlist());

View File

@@ -36,6 +36,7 @@ import static java.lang.Boolean.FALSE;
import android.app.AppGlobals;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
@@ -44,6 +45,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseIntArray;
import androidx.annotation.NonNull;
@@ -71,6 +73,7 @@ public class UserAspectRatioManager {
private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true;
final boolean mIsUserMinAspectRatioAppDefaultFlagEnabled = Flags.userMinAspectRatioAppDefault();
private final boolean mIgnoreActivityOrientationRequest;
private final Context mContext;
private final IPackageManager mIPm;
@@ -90,6 +93,8 @@ public class UserAspectRatioManager {
mUserAspectRatioA11yMap = new ArrayMap<>();
mUserAspectRatioOrder = new SparseIntArray();
mUserAspectRatioMap = getUserMinAspectRatioMapping();
mIgnoreActivityOrientationRequest = getValueFromDeviceConfig(
"ignore_activity_orientation_request", false);
}
/**
@@ -113,6 +118,24 @@ public class UserAspectRatioManager {
? aspectRatio : USER_MIN_ASPECT_RATIO_UNSET;
}
// TODO b/374903057 reuse method from ActivityRecord
boolean isUniversalResizeable(@NonNull String packageName, int userId) {
try {
final ApplicationInfo info = mIPm.getApplicationInfo(
packageName, 0 /* flags */, userId);
if (info == null || info.category == ApplicationInfo.CATEGORY_GAME) {
return false;
}
final boolean compatEnabled = Flags.universalResizableByDefault()
&& info.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
return compatEnabled || mIgnoreActivityOrientationRequest;
} catch (RemoteException e) {
Log.e("UserAspectRatioManager", "Could not access application info for "
+ packageName + ":\n" + e);
return false;
}
}
/**
* @return corresponding string for {@link PackageManager.UserMinAspectRatio} value
*/
@@ -127,7 +150,7 @@ public class UserAspectRatioManager {
return appDefault;
}
return isCurrentSelectionFromManufacturerOverride(packageName, userId, aspectRatio)
return isUnsetAndRequiresFullscreenOverride(packageName, userId, aspectRatio)
? getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, packageName, userId)
: mUserAspectRatioMap.getOrDefault(aspectRatio, appDefault);
}
@@ -139,7 +162,7 @@ public class UserAspectRatioManager {
public CharSequence getAccessibleEntry(@PackageManager.UserMinAspectRatio int aspectRatio,
@NonNull String packageName) {
final int userId = mContext.getUserId();
return isCurrentSelectionFromManufacturerOverride(packageName, userId, aspectRatio)
return isUnsetAndRequiresFullscreenOverride(packageName, userId, aspectRatio)
? getAccessibleEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, packageName)
: mUserAspectRatioA11yMap.getOrDefault(aspectRatio,
getUserMinAspectRatioEntry(aspectRatio, packageName, userId));
@@ -203,7 +226,7 @@ public class UserAspectRatioManager {
@PackageManager.UserMinAspectRatio int userOverride) {
return (userOverride != USER_MIN_ASPECT_RATIO_UNSET
&& userOverride != USER_MIN_ASPECT_RATIO_APP_DEFAULT)
|| isCurrentSelectionFromManufacturerOverride(app.packageName, getUserId(app.uid),
|| isUnsetAndRequiresFullscreenOverride(app.packageName, getUserId(app.uid),
userOverride);
}
@@ -224,7 +247,7 @@ public class UserAspectRatioManager {
/**
* Whether the device manufacturer has overridden app's orientation to
* {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER} to force app to fullscreen
* and app has not opted-out from the treatment
* or app is universal resizeable, and app has not opted-out from the treatment
*/
boolean isOverrideToFullscreenEnabled(String pkgName, int userId) {
Boolean appAllowsOrientationOverride = readComponentProperty(mContext.getPackageManager(),
@@ -232,7 +255,8 @@ public class UserAspectRatioManager {
return mIsUserMinAspectRatioAppDefaultFlagEnabled
&& hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, pkgName)
&& !FALSE.equals(appAllowsOrientationOverride)
&& isFullscreenCompatChangeEnabled(pkgName, userId);
&& (isFullscreenCompatChangeEnabled(pkgName, userId)
|| isUniversalResizeable(pkgName, userId));
}
boolean isFullscreenCompatChangeEnabled(String pkgName, int userId) {
@@ -240,7 +264,11 @@ public class UserAspectRatioManager {
OVERRIDE_ANY_ORIENTATION_TO_USER, pkgName, UserHandle.of(userId));
}
private boolean isCurrentSelectionFromManufacturerOverride(String pkgName, int userId,
/**
* Whether the aspect ratio is unset and we desire to interpret it as fullscreen rather than
* app default because of manufacturer override or because the app is universal resizeable
*/
private boolean isUnsetAndRequiresFullscreenOverride(String pkgName, int userId,
@PackageManager.UserMinAspectRatio int aspectRatio) {
return aspectRatio == USER_MIN_ASPECT_RATIO_UNSET
&& isOverrideToFullscreenEnabled(pkgName, userId);

View File

@@ -53,6 +53,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.InstrumentedPreferenceFragment;
@@ -240,13 +241,21 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
showDialogInner(ButtonActionDialogFragment.DialogType.DISABLE);
} else if (mAppEntry.info.enabled) {
requireAuthAndExecute(() -> {
mMetricsFeatureProvider.action(
mActivity,
SettingsEnums.ACTION_SETTINGS_DISABLE_APP,
getPackageNameForMetric());
AsyncTask.execute(new DisableChangerRunnable(mPm,
mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT));
});
} else {
mMetricsFeatureProvider.action(
mActivity,
mAppEntry.info.enabled
? SettingsEnums.ACTION_SETTINGS_DISABLE_APP
: SettingsEnums.ACTION_SETTINGS_ENABLE_APP,
getPackageNameForMetric());
SettingsEnums.ACTION_SETTINGS_ENABLE_APP,
getPackageNameForMetric());
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT));
}
@@ -289,17 +298,34 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp
}
}
/**
* Runs the given action with restricted lock authentication if it is a protected package.
*
* @param action The action to run.
*/
private void requireAuthAndExecute(Runnable action) {
if (Utils.isProtectedPackage(mContext, mAppEntry.info.packageName)) {
AppInfoDashboardFragment.showLockScreen(mContext, () -> action.run());
} else {
action.run();
}
}
public void handleDialogClick(int id) {
switch (id) {
case ButtonActionDialogFragment.DialogType.DISABLE:
mMetricsFeatureProvider.action(mActivity,
SettingsEnums.ACTION_SETTINGS_DISABLE_APP,
getPackageNameForMetric());
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER));
requireAuthAndExecute(() -> {
mMetricsFeatureProvider.action(mActivity,
SettingsEnums.ACTION_SETTINGS_DISABLE_APP,
getPackageNameForMetric());
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER));
});
break;
case ButtonActionDialogFragment.DialogType.FORCE_STOP:
forceStopPackage(mAppEntry.info.packageName);
requireAuthAndExecute(() -> {
forceStopPackage(mAppEntry.info.packageName);
});
break;
}
}
@@ -535,14 +561,16 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp
@VisibleForTesting
void uninstallPkg(String packageName, boolean allUsers) {
stopListeningToPackageRemove();
// Create new intent to launch Uninstaller activity
Uri packageUri = Uri.parse("package:" + packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
requireAuthAndExecute(() -> {
stopListeningToPackageRemove();
// Create new intent to launch Uninstaller activity
Uri packageUri = Uri.parse("package:" + packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
mMetricsFeatureProvider.action(mActivity, SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP);
mFragment.startActivityForResult(uninstallIntent, mRequestUninstall);
mMetricsFeatureProvider.action(mActivity, SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP);
mFragment.startActivityForResult(uninstallIntent, mRequestUninstall);
});
}
@VisibleForTesting

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2024 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.applications.appinfo;
import android.content.Context;
import androidx.annotation.NonNull;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.flags.Flags;
/**
* A controller to update current locale information of application
* and a entry to launch {@link ManageApplications}.
*/
public class AppsLocalePreferenceController extends BasePreferenceController {
public AppsLocalePreferenceController(@NonNull Context context, @NonNull String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
if (!Flags.regionalPreferencesApiEnabled()) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -17,6 +17,8 @@ package com.android.settings.applications.appinfo;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES;
import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY;
import android.app.AppOpsManager;
import android.app.settings.SettingsEnums;
@@ -34,6 +36,7 @@ import com.android.settings.Settings;
import com.android.settings.applications.AppInfoWithHeader;
import com.android.settings.applications.AppStateInstallAppsBridge;
import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -82,15 +85,34 @@ public class ExternalSourcesDetails extends AppInfoWithHeader
public static CharSequence getPreferenceSummary(Context context, AppEntry entry) {
final UserHandle userHandle = UserHandle.getUserHandleForUid(entry.info.uid);
final UserManager um = UserManager.get(context);
final int userRestrictionSource = um.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userHandle)
| um.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
userHandle);
if ((userRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
return context.getString(com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
} else if (userRestrictionSource != 0) {
return context.getString(com.android.settingslib.R.string.disabled);
if (android.security.Flags.aapmFeatureDisableInstallUnknownSources()) {
if (um.hasBaseUserRestriction(DISALLOW_INSTALL_UNKNOWN_SOURCES, userHandle)) {
return context.getString(com.android.settingslib.R.string.disabled);
} else if (um.hasUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES, userHandle)) {
return context.getString(
com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
} else if (um.hasUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
userHandle)) {
if (RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(context,
DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userHandle.getIdentifier())) {
return context.getString(com.android.settingslib.widget.restricted
.R.string.disabled_by_advanced_protection);
} else {
return context.getString(
com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
}
}
} else {
final int userRestrictionSource = um.getUserRestrictionSource(
DISALLOW_INSTALL_UNKNOWN_SOURCES, userHandle)
| um.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userHandle);
if ((userRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
return context.getString(
com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
} else if (userRestrictionSource != 0) {
return context.getString(com.android.settingslib.R.string.disabled);
}
}
final InstallAppsState appsState = new AppStateInstallAppsBridge(context, null, null)
.createInstallAppsStateFor(entry.info.packageName, entry.info.uid);
@@ -110,14 +132,14 @@ public class ExternalSourcesDetails extends AppInfoWithHeader
if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
return false;
}
if (mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
if (mUserManager.hasBaseUserRestriction(DISALLOW_INSTALL_UNKNOWN_SOURCES,
UserHandle.of(UserHandle.myUserId()))) {
mSwitchPref.setChecked(false);
mSwitchPref.setSummary(com.android.settingslib.R.string.disabled);
mSwitchPref.setEnabled(false);
return true;
}
mSwitchPref.checkRestrictionAndSetDisabled(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
mSwitchPref.checkRestrictionAndSetDisabled(DISALLOW_INSTALL_UNKNOWN_SOURCES);
if (!mSwitchPref.isDisabledByAdmin()) {
mSwitchPref.checkRestrictionAndSetDisabled(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2024 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.applications.appinfo;
import android.content.Context;
import androidx.annotation.NonNull;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.flags.Flags;
/**
* A controller to update current locale information of application
* and a entry to launch {@link ManageApplications}.
*/
public class NewAppsLocalePreferenceController extends BasePreferenceController {
public NewAppsLocalePreferenceController(@NonNull Context context, @NonNull String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
// TODO(b/381011808) After feature release, this class may be renamed.
if (Flags.regionalPreferencesApiEnabled()) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2024 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.applications.contacts;
import static android.provider.ContactsContract.RawContacts.DefaultAccount;
import android.accounts.Account;
import android.content.Context;
import android.os.UserHandle;
import android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState;
import android.provider.Flags;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.accounts.AuthenticatorHelper;
/**
* A preference controller handling the logic for updating summary of contacts default account.
*/
public class ContactsStoragePreferenceController extends BasePreferenceController {
private static final String TAG = "ContactsStorageController";
private final AuthenticatorHelper mAuthenticatorHelper;
private DefaultAccountAndState mCurrentDefaultAccountAndState;
public ContactsStoragePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mAuthenticatorHelper = new AuthenticatorHelper(mContext,
new UserHandle(UserHandle.myUserId()), null);
try {
mCurrentDefaultAccountAndState =
DefaultAccount.getDefaultAccountForNewContacts(mContext.getContentResolver());
} catch (IllegalStateException e) {
Log.e(TAG, "The default account is in an invalid state: " + e);
} catch (RuntimeException e) {
Log.e(TAG, "Failed to look up the default account: " + e);
}
}
@Override
public int getAvailabilityStatus() {
return (Flags.newDefaultAccountApiEnabled()
&& mCurrentDefaultAccountAndState != null) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
public CharSequence getSummary() {
if (mCurrentDefaultAccountAndState != null) {
// Re-fetch account in controller to refresh the latest set default account.
mCurrentDefaultAccountAndState =
DefaultAccount.getDefaultAccountForNewContacts(mContext.getContentResolver());
int currentDefaultAccountState = mCurrentDefaultAccountAndState.getState();
Account currentDefaultAccount = mCurrentDefaultAccountAndState.getAccount();
if (currentDefaultAccountState
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_NOT_SET) {
return mContext.getResources().getString(
R.string.contacts_storage_no_account_set_summary);
} else if (currentDefaultAccountState
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_LOCAL) {
return mContext.getResources().getString(
R.string.contacts_storage_local_account_summary);
} else if (currentDefaultAccountState
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_SIM) {
return mContext.getResources().getString(
R.string.sim_card_label);
} else if (currentDefaultAccountState
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD) {
String accountTypeLabel = (String) mAuthenticatorHelper.getLabelForType(mContext,
currentDefaultAccount.type);
// If there's no account type, or the account type is the same as the
// current default account name, just return the account name.
if (accountTypeLabel == null || accountTypeLabel.equals(
currentDefaultAccount.name)) {
return currentDefaultAccount.name;
}
return accountTypeLabel + " | " + currentDefaultAccount.name;
}
}
return "";
}
}

View File

@@ -0,0 +1,305 @@
/*
* Copyright (C) 2020 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.applications.contacts;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.ContactsContract.RawContacts.DefaultAccount;
import static android.provider.Settings.ACTION_ADD_ACCOUNT;
import static android.provider.Settings.EXTRA_ACCOUNT_TYPES;
import android.accounts.Account;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceGroup;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.accounts.AddAccountSettings;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.accounts.AuthenticatorHelper;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Settings page for contacts default account
*/
@SearchIndexable
public class ContactsStorageSettings extends DashboardFragment
implements SelectorWithWidgetPreference.OnClickListener, OnPreferenceClickListener,
AuthenticatorHelper.OnAccountsUpdateListener {
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.contacts_storage_settings);
private static final String TAG = "ContactsStorageSettings";
private static final String PREF_KEY_ADD_ACCOUNT = "add_account";
private static final String PREF_KEY_DEVICE_ONLY = "device_only_account_preference";
private static final String PREF_KEY_ACCOUNT_CATEGORY = "account_category";
private final Map<String, DefaultAccountAndState> mAccountMap = new HashMap<>();
private AuthenticatorHelper mAuthenticatorHelper;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mAuthenticatorHelper = new AuthenticatorHelper(context,
new UserHandle(UserHandle.myUserId()), this);
mAuthenticatorHelper.listenToAccountUpdates();
preloadEligibleAccountIcon();
}
@Override
public void onDetach() {
super.onDetach();
mAuthenticatorHelper.stopListeningToAccountUpdates();
}
@UiThread
@Override
public void onRadioButtonClicked(@NonNull SelectorWithWidgetPreference selectedPref) {
final String selectedPreferenceKey = selectedPref.getKey();
// Check if current account is different from the selected account.
for (String preferenceKey : mAccountMap.keySet()) {
if (selectedPreferenceKey.equals(preferenceKey)) {
try {
DefaultAccountAndState currentDefaultAccount = mAccountMap.get(preferenceKey);
DefaultAccount.setDefaultAccountForNewContacts(getContentResolver(),
currentDefaultAccount);
selectedPref.setChecked(true);
if (currentDefaultAccount.getState()
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD) {
startMoveLocalAndSimContactsActivity();
}
} catch (RuntimeException e) {
Log.e(TAG, "Error setting the default account " + e);
Toast.makeText(getContext(),
R.string.contacts_storage_set_default_account_error_message,
Toast.LENGTH_SHORT).show();
}
} else {
SelectorWithWidgetPreference unSelectedPreference =
getPreferenceScreen().findPreference(preferenceKey);
if (unSelectedPreference != null) {
unSelectedPreference.setChecked(false);
}
}
}
}
public boolean onPreferenceClick(@NonNull Preference preference) {
if (PREF_KEY_ADD_ACCOUNT.equals(preference.getKey())) {
String[] accountTypesArray = getEligibleAccountTypes();
Intent intent = new Intent(ACTION_ADD_ACCOUNT);
intent.setClass(getContext(), AddAccountSettings.class);
intent.putExtra(EXTRA_ACCOUNT_TYPES, accountTypesArray);
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
return true;
}
return false;
}
@Override
public void onAccountsUpdate(UserHandle userHandle) {
preloadEligibleAccountIcon();
refreshUI();
}
@Override
public void onCreatePreferences(@NonNull Bundle savedInstanceState,
@NonNull String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
refreshUI();
}
@UiThread
void refreshUI() {
// Clear all the accounts stored in the map and later on re-fetch the eligible accounts
// when creating eligible account preferences.
mAccountMap.clear();
final PreferenceGroup preferenceGroup = findPreference(PREF_KEY_ACCOUNT_CATEGORY);
preferenceGroup.removeAll();
// If the default account is SIM, we should show in the page, otherwise don't show.
SelectorWithWidgetPreference simAccountPreference = buildSimAccountPreference();
if (simAccountPreference != null) {
preferenceGroup.addPreference(simAccountPreference);
}
List<Account> accounts = DefaultAccount.getEligibleCloudAccounts(getContentResolver());
for (int i = 0; i < accounts.size(); i++) {
preferenceGroup.addPreference(
buildCloudAccountPreference(accounts.get(i), /*order=*/i));
}
// If there's no eligible account types, the "Add Account" preference should
// not be shown to the users.
if (getEligibleAccountTypes().length > 0) {
preferenceGroup.addPreference(buildAddAccountPreference(accounts.isEmpty()));
}
setupDeviceOnlyPreference();
setDefaultAccountPreference(preferenceGroup);
}
private void preloadEligibleAccountIcon() {
String[] accountTypes = getEligibleAccountTypes();
for (String accountType : accountTypes) {
// Preload the drawable for the account type to avoid the latency when rendering the
// account preference.
mAuthenticatorHelper.preloadDrawableForType(getContext(), accountType);
}
}
private void setupDeviceOnlyPreference() {
SelectorWithWidgetPreference preference = findPreference(PREF_KEY_DEVICE_ONLY);
if (preference != null) {
preference.setOnClickListener(this);
mAccountMap.put(PREF_KEY_DEVICE_ONLY, DefaultAccountAndState.ofLocal());
}
}
private void setDefaultAccountPreference(PreferenceGroup preferenceGroup) {
DefaultAccountAndState currentDefaultAccountAndState =
DefaultAccount.getDefaultAccountForNewContacts(getContentResolver());
String preferenceKey = getAccountHashCode(currentDefaultAccountAndState);
Account currentDefaultAccount = currentDefaultAccountAndState.getAccount();
// Set the current default account preference to be checked if found among existing
// preferences. If not, then create a new preference for default account.
SelectorWithWidgetPreference preference = null;
if (mAccountMap.containsKey(preferenceKey)) {
preference = getPreferenceScreen().findPreference(preferenceKey);
} else if (preferenceKey != null && currentDefaultAccount != null) {
preference = buildCloudAccountPreference(currentDefaultAccount, mAccountMap.size());
preferenceGroup.addPreference(preference);
}
if (preference != null) {
preference.setChecked(true);
}
}
private SelectorWithWidgetPreference buildCloudAccountPreference(Account account, int order) {
SelectorWithWidgetPreference preference = new SelectorWithWidgetPreference(
getPrefContext());
DefaultAccountAndState accountAndState = DefaultAccountAndState.ofCloud(account);
String preferenceKey = getAccountHashCode(accountAndState);
String accountPreferenceTitle = getString(R.string.contacts_storage_account_title,
mAuthenticatorHelper.getLabelForType(getPrefContext(), account.type));
preference.setTitle(accountPreferenceTitle);
preference.setIcon(mAuthenticatorHelper.getDrawableForType(getPrefContext(), account.type));
preference.setSummary(account.name);
preference.setKey(preferenceKey);
preference.setOnClickListener(this);
preference.setOrder(order);
mAccountMap.put(preferenceKey, accountAndState);
return preference;
}
@Nullable
private SelectorWithWidgetPreference buildSimAccountPreference() {
DefaultAccountAndState currentDefaultAccountAndState =
DefaultAccount.getDefaultAccountForNewContacts(getContentResolver());
if (currentDefaultAccountAndState.getState()
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_SIM) {
String preferenceKey = getAccountHashCode(currentDefaultAccountAndState);
SelectorWithWidgetPreference preference = new SelectorWithWidgetPreference(
getPrefContext());
preference.setTitle(R.string.sim_card_label);
preference.setIcon(R.drawable.ic_sim_card);
preference.setSummary(R.string.sim_card_label);
preference.setKey(preferenceKey);
preference.setOnClickListener(this);
mAccountMap.put(preferenceKey, currentDefaultAccountAndState);
return preference;
}
return null;
}
private RestrictedPreference buildAddAccountPreference(boolean noAccountBeenAdded) {
RestrictedPreference preference = new RestrictedPreference(getPrefContext());
preference.setKey(PREF_KEY_ADD_ACCOUNT);
if (noAccountBeenAdded) {
preference.setTitle(R.string.contacts_storage_first_time_add_account_message);
} else {
preference.setTitle(R.string.add_account_label);
}
preference.setIcon(R.drawable.ic_add_24dp);
preference.setOnPreferenceClickListener(this);
preference.setOrder(998);
return preference;
}
private void startMoveLocalAndSimContactsActivity() {
Intent intent = new Intent()
.setAction(DefaultAccount.ACTION_MOVE_CONTACTS_TO_DEFAULT_ACCOUNT)
.setPackage("com.android.providers.contacts")
.addFlags(FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
}
@Nullable
private String getAccountHashCode(
DefaultAccountAndState currentDefaultAccountAndState) {
Account currentDefaultAccount = currentDefaultAccountAndState.getAccount();
if (currentDefaultAccount != null && (currentDefaultAccountAndState.getState()
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD
|| currentDefaultAccountAndState.getState()
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_SIM)) {
return String.valueOf(currentDefaultAccount.hashCode());
} else if (currentDefaultAccountAndState.getState()
== DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_LOCAL) {
return PREF_KEY_DEVICE_ONLY;
} else {
// If the account is not set or in error state, it should just return null and won't
// set the checked status in radio button.
return null;
}
}
@VisibleForTesting
String[] getEligibleAccountTypes() {
return Resources.getSystem().getStringArray(
com.android.internal.R.array.config_rawContactsEligibleDefaultAccountTypes);
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.contacts_storage_settings;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.CONTACTS_STORAGE;
}
}

View File

@@ -23,10 +23,10 @@ import android.os.Bundle;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
@@ -46,13 +46,16 @@ import java.util.List;
* Therefore, as a simple workaround, we use a new class which is enabled by default.
*/
@SearchIndexable
public class UserBackupSettingsActivity extends FragmentActivity implements Indexable {
public class UserBackupSettingsActivity extends SettingsActivity implements Indexable {
private static final String TAG = "BackupSettingsActivity";
private FragmentManager mFragmentManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFinishing()) {
return;
}
BackupSettingsHelper backupHelper = new BackupSettingsHelper(this);

View File

@@ -148,8 +148,16 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
ThemeHelper.trySetDynamicColor(this);
if (ThemeHelper.shouldApplyGlifExpressiveStyle(getApplicationContext())) {
if (!ThemeHelper.trySetSuwTheme(this)) {
setTheme(ThemeHelper.getSuwDefaultTheme(getApplicationContext()));
ThemeHelper.trySetDynamicColor(this);
}
} else {
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
ThemeHelper.trySetDynamicColor(this);
}
mChallenge = getIntent().getLongExtra(EXTRA_KEY_CHALLENGE, -1L);
mSensorId = getIntent().getIntExtra(EXTRA_KEY_SENSOR_ID, -1);
// Don't need to retrieve the HAT if it already exists. In some cases, the extras do not

View File

@@ -37,6 +37,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.Utils;
import com.android.settings.flags.Flags;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmDeviceCredentialActivity;
@@ -551,8 +552,11 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
@NonNull
protected PorterDuffColorFilter getIconColorFilter() {
if (mIconColorFilter == null) {
final int colorType = Flags.biometricsOnboardingEducation()
? DynamicColorPalette.ColorType.PRIMARY_TEXT
: DynamicColorPalette.ColorType.ACCENT;
mIconColorFilter = new PorterDuffColorFilter(
DynamicColorPalette.getColor(this, DynamicColorPalette.ColorType.ACCENT),
DynamicColorPalette.getColor(this, colorType),
PorterDuff.Mode.SRC_IN);
}
return mIconColorFilter;

View File

@@ -53,6 +53,7 @@ import com.android.settings.biometrics.BiometricEnrollActivity;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.MultiBiometricEnrollHelper;
import com.android.settings.flags.Flags;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.SetupSkipDialog;
import com.android.settings.utils.SensorPrivacyManagerHelper;
@@ -144,6 +145,19 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
final ImageView iconLooking = findViewById(R.id.icon_looking);
iconGlasses.getBackground().setColorFilter(getIconColorFilter());
iconLooking.getBackground().setColorFilter(getIconColorFilter());
if (Flags.biometricsOnboardingEducation()) {
final ImageView iconSecurityPrivacySafe = findViewById(R.id.icon_security_privacy_safe);
final ImageView iconPrivacyTip = findViewById(R.id.icon_privacy_tip);
final ImageView iconFamiliarFaceAndZone =
findViewById(R.id.icon_familiar_face_and_zone);
final ImageView iconTrashCan = findViewById(R.id.icon_trash_can);
final ImageView iconLink = findViewById(R.id.icon_link);
iconSecurityPrivacySafe.getBackground().setColorFilter(getIconColorFilter());
iconPrivacyTip.getBackground().setColorFilter(getIconColorFilter());
iconFamiliarFaceAndZone.getBackground().setColorFilter(getIconColorFilter());
iconTrashCan.getBackground().setColorFilter(getIconColorFilter());
iconLink.getBackground().setColorFilter(getIconColorFilter());
}
// Set text for views with multiple variations.
final TextView infoMessageGlasses = findViewById(R.id.info_message_glasses);
@@ -156,9 +170,19 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
infoMessageLooking.setText(getInfoMessageLooking());
inControlTitle.setText(getInControlTitle());
howMessage.setText(getHowMessage());
inControlMessage.setText(Html.fromHtml(getString(getInControlMessage()),
Html.FROM_HTML_MODE_LEGACY));
inControlMessage.setMovementMethod(LinkMovementMethod.getInstance());
if (Flags.biometricsOnboardingEducation()) {
inControlMessage.setText(
R.string.security_settings_face_enroll_introduction_control_message_2);
final TextView learnMore = findViewById(R.id.message_learn_more);
learnMore.setText(Html.fromHtml(getString(
R.string.security_settings_face_enroll_introduction_learn_more_message),
Html.FROM_HTML_MODE_LEGACY));
learnMore.setMovementMethod(LinkMovementMethod.getInstance());
} else {
inControlMessage.setText(Html.fromHtml(getString(getInControlMessage()),
Html.FROM_HTML_MODE_LEGACY));
inControlMessage.setMovementMethod(LinkMovementMethod.getInstance());
}
lessSecure.setText(getLessSecureMessage());
final ScrollView scrollView =
@@ -411,7 +435,11 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
@Override
protected int getLayoutResource() {
return R.layout.face_enroll_introduction;
if (Flags.biometricsOnboardingEducation()) {
return R.layout.face_enroll_introduction_2;
} else {
return R.layout.face_enroll_introduction;
}
}
@Override
@@ -594,8 +622,13 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
setDescriptionText(getString(
R.string.private_space_face_enroll_introduction_message));
} else if (mIsFaceStrong) {
setDescriptionText(getString(
R.string.security_settings_face_enroll_introduction_message_class3));
final int messageRes;
if (Flags.biometricsOnboardingEducation()) {
messageRes = R.string.security_settings_face_enroll_introduction_message_class3_2;
} else {
messageRes = R.string.security_settings_face_enroll_introduction_message_class3;
}
setDescriptionText(getString(messageRes));
}
super.updateDescriptionText();
}

Some files were not shown because too many files have changed in this diff Show More