Snap for 11591048 from 59a329cae5 to 24Q3-release

Change-Id: I70f629898beaf25d659539a215056bc7245fcef1
This commit is contained in:
Android Build Coastguard Worker
2024-03-18 23:21:30 +00:00
29 changed files with 1474 additions and 110 deletions

View File

@@ -1,6 +1,13 @@
package: "com.android.settings.development"
container: "system"
flag {
name: "a2dp_offload_codec_extensibility_settings"
namespace: "bluetooth"
description: "Feature flag for Bluetooth Audio Codec extensibility in Settings"
bug: "323319530"
}
flag {
name: "deprecate_list_activity"
namespace: "android_settings"

View File

@@ -178,6 +178,9 @@
<!-- Description for text in accessibility hearing aids footer. [CHAR LIMIT=NONE] -->
<string name="bluetooth_audio_routing_footer_summary">By default, audio output is determined by individual apps</string>
<!-- Bluetooth audio codec related settings. Title of the default audio codec selection. [CHAR LIMIT=60] -->
<string name="bluetooth_audio_codec_default_selection">Use System Selection (Default)</string>
<!--Bluetooth settings screen, summary text for Bluetooth device with no name -->
<string name="bluetooth_device">Unnamed Bluetooth device</string>
<!--Bluetooth settings screen, text that appears in heading bar when scanning for devices -->
@@ -1551,20 +1554,12 @@
<string name="unlock_disable_frp_warning_content_pattern_face_fingerprint">"A pattern protects your phone if it\u2019s lost or stolen.<xliff:g id="empty_line">\n\n</xliff:g>This deletes the fingerprint model stored on your device. Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face or fingerprint for authentication in apps."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin">"A PIN protects your phone if it\u2019s lost or stolen"</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_authbound_keys">"You will lose saved data like your PIN.<xliff:g id="empty_line">\n\n</xliff:g>Cards set up for tap to pay will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Wallets and other apps that require device unlock may not work properly."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has fingerprints enrolled [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_fingerprint">"A PIN protects your phone if it\u2019s lost or stolen.<xliff:g id="empty_line">\n\n</xliff:g>This also deletes the fingerprint model stored on your device. You won\u2019t be able to use your fingerprint for authentication in apps."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has fingerprints enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_fingerprint_authbound_keys">"You will lose saved data like your PIN and fingerprint model.<xliff:g id="empty_line">\n\n</xliff:g>Cards set up for tap to pay will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Wallets and other apps that require device unlock may not work properly."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has face enrolled [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_face">"A PIN protects your phone if it\u2019s lost or stolen.<xliff:g id="empty_line">\n\n</xliff:g>Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face for authentication in apps."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has face enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_face_authbound_keys">"You will lose saved data like your PIN and face model.<xliff:g id="empty_line">\n\n</xliff:g>Cards set up for tap to pay will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Wallets and other apps that require device unlock may not work properly."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has face authentication and fingerprint enrolled [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_face_fingerprint">"A PIN protects your phone if it\u2019s lost or stolen.<xliff:g id="empty_line">\n\n</xliff:g>This deletes the fingerprint model stored on your device. Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face or fingerprint for authentication in apps."</string>
<!-- Content of the dialog shown when the user removes the device lock PIN and the user has face authentication and fingerprint enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_pin_face_fingerprint_authbound_keys">"You will lose saved data like your PIN, face and fingerprint models.<xliff:g id="empty_line">\n\n</xliff:g>Cards set up for tap to pay will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Wallets and other apps that require device unlock may not work properly."</string>
<!-- Content of the dialog shown when the user removes the device lock password [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_password">"A password protects your phone if it\u2019s lost or stolen"</string>
<!-- Content of the dialog shown when the user removes the device lock password and the user has fingerprints enrolled [CHAR LIMIT=NONE] -->
@@ -1581,6 +1576,15 @@
<string name="unlock_disable_frp_warning_content_unknown_face">"Device protection features will not work without your screen lock.<xliff:g id="empty_line">\n\n</xliff:g>Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face for authentication in apps."</string>
<!-- Content of the dialog shown when the user removes the device lock of unknown type and the user has face authentication and fingerprint enrolled [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_unknown_face_fingerprint">"Device protection features will not work without your screen lock.<xliff:g id="empty_line">\n\n</xliff:g>This deletes the fingerprint model stored on your device. Your face model will also be permanently and securely deleted. You won\u2019t be able to use your face or fingerprint for authentication in apps."</string>
<!-- Content of the dialog shown when the user removes any device screenlock and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_authbound_keys">"Your screen lock will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Tap to pay won\u2019t be available.<xliff:g id="empty_line">\n\n</xliff:g>Wallet, payment, and other apps that require authentication may not work properly."</string>
<!-- Content of the dialog shown when the user removes any device screen lock and the user has fingerprints enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_fingerprint_authbound_keys">"Your screen lock and Fingerprint Unlock will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Tap to pay won\u2019t be available.<xliff:g id="empty_line">\n\n</xliff:g>Wallet, payment, and other apps that require authentication may not work properly."</string>
<!-- Content of the dialog shown when the user removes any device screen lock and the user has face enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_face_authbound_keys">"Your screen lock and Face Unlock will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Tap to pay won\u2019t be available.<xliff:g id="empty_line">\n\n</xliff:g>Wallet, payment, and other apps that require authentication may not work properly."</string>
<!-- Content of the dialog shown when the user removes any device screen lock and the user has face authentication and fingerprint enrolled and there are apps with auth-bound keys that will be affected [CHAR LIMIT=NONE] -->
<string name="unlock_disable_frp_warning_content_face_fingerprint_authbound_keys">"Your screen lock and Face &amp; Fingerprint Unlock will be removed.<xliff:g id="empty_line">\n\n</xliff:g>Tap to pay won\u2019t be available.<xliff:g id="empty_line">\n\n</xliff:g>Wallet, payment, and other apps that require authentication may not work properly."</string>
<!-- Affirmative action of the dialog shown when the user removes the device lock [CHAR LIMIT=25] -->
<string name="unlock_disable_frp_warning_ok">Delete</string>
@@ -2320,6 +2324,8 @@
<string name="wifi_details_title">Network details</string>
<!-- Wifi details preference title to display router IP subnet mask -->
<string name="wifi_details_subnet_mask">Subnet mask</string>
<!-- Server name title-->
<string name="server_name_title">Server name</string>
<!-- Wifi details preference title to display wifi type info [CHAR LIMIT=50]-->
<string name="wifi_type_title">Type</string>
<!-- Wifi details preference title to display router DNS info -->
@@ -3198,9 +3204,18 @@
<!-- Body of dialog confirming that user wants to forget an internal storage device [CHAR LIMIT=NONE]-->
<string name="storage_internal_forget_confirm">All the apps, photos, and data stored on this <xliff:g id="name" example="SD card">^1</xliff:g> will be lost forever.</string>
<!-- Body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]-->
<!-- Old body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]-->
<string name="storage_detail_dialog_system">System includes files used to run Android version <xliff:g id="version" example="8.0">%s</xliff:g></string>
<!-- New body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]-->
<string name="storage_os_detail_dialog_system">This includes your operating system and the files that are needed to keep your phone running smoothly. To protect their integrity, these files can\u2019t be accessed.</string>
<!-- Body of dialog informing user about the storage used by the Android temporary system files [CHAR LIMIT=NONE]-->
<string name="storage_other_files_detail_dialog_system">This includes cache and other temporary files that are needed by your operating system. You may notice changes to the amount of storage used over time.</string>
<!-- Label for categories splitter in Settings > Storage [CHAR LIMIT=none] -->
<string name="storage_system_label">System</string>
<!-- Message to notify guest users as to why they can't set up the storage device [CHAR LIMIT=50]-->
<string name="storage_wizard_guest">Guest mode users cannot format SD cards</string>
@@ -10855,9 +10870,15 @@
<!-- Preference label for the Documents & other storage section. [CHAR LIMIT=50] -->
<string name="storage_documents_and_other">Documents &amp; other</string>
<!-- Preference label for the System storage section. [CHAR LIMIT=50] -->
<!-- Old Preference label for the System storage section. [CHAR LIMIT=50] -->
<string name="storage_system">System</string>
<!-- New Preference label for the System storage section. [CHAR LIMIT=50] -->
<string name="storage_os_name">Android <xliff:g id="version" example="8">%s</xliff:g></string>
<!-- Preference label for the System storage section. [CHAR LIMIT=50] -->
<string name="storage_temporary_files">Temporary system files</string>
<!-- Preference label for the Trash storage section. [CHAR LIMIT=50] -->
<string name="storage_trash">Trash</string>

View File

@@ -430,6 +430,11 @@
android:positiveButtonText=""
android:negativeButtonText="@string/dlg_ok"/>
<ListPreference
android:key="bluetooth_audio_codec_settings_list"
android:title="@string/bluetooth_select_a2dp_codec_type"
android:dialogTitle="@string/bluetooth_select_a2dp_codec_type_dialog_title"/>
<com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreference
android:key="bluetooth_sample_rate_settings"
android:title="@string/bluetooth_select_a2dp_codec_sample_rate"

View File

@@ -42,7 +42,8 @@ public final class PreferredShortcuts {
/**
* Retrieves the user preferred shortcut types for the given {@code componentName} from
* SharedPreferences.
* SharedPreferences. If the user doesn't have a preferred shortcut,
* {@link ShortcutConstants.UserShortcutType.SOFTWARE} is returned.
*
* @param context {@link Context} to access the {@link SharedPreferences}
* @param componentName Name of the service or activity, should be the format of {@link
@@ -52,7 +53,26 @@ public final class PreferredShortcuts {
@ShortcutConstants.UserShortcutType
public static int retrieveUserShortcutType(
@NonNull Context context, @NonNull String componentName) {
final int defaultTypes = getDefaultPreferredShortcutTypesForTarget(componentName);
return retrieveUserShortcutType(
context, componentName, ShortcutConstants.UserShortcutType.SOFTWARE);
}
/**
* Retrieves the user preferred shortcut types for the given {@code componentName} from
* SharedPreferences.
*
* @param context {@link Context} to access the {@link SharedPreferences}
* @param componentName Name of the service or activity, should be the format of {@link
* ComponentName#flattenToString()}.
* @param defaultTypes The default shortcut types to use if the user doesn't have a
* preferred shortcut.
* @return {@link ShortcutConstants.UserShortcutType}
*/
@ShortcutConstants.UserShortcutType
public static int retrieveUserShortcutType(
@NonNull Context context,
@NonNull String componentName,
@ShortcutConstants.UserShortcutType int defaultTypes) {
// Create a mutable set to modify
final Set<String> info = new HashSet<>(getFromSharedPreferences(context));
@@ -150,14 +170,5 @@ public final class PreferredShortcuts {
getSharedPreferences(context).edit().clear().apply();
}
/**
* Returns the default shortcut types for the given accessibility feature.
*/
@ShortcutConstants.UserShortcutType
private static int getDefaultPreferredShortcutTypesForTarget(@NonNull String componentName) {
// TODO (b/322712028): return different default shortcut types for the given component
return ShortcutConstants.UserShortcutType.SOFTWARE;
}
private PreferredShortcuts() {}
}

View File

@@ -45,6 +45,7 @@ import android.widget.CompoundButton;
import androidx.annotation.Nullable;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
@@ -330,7 +331,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
@Override
public void onToggleClicked(ShortcutPreference preference) {
final int shortcutTypes = retrieveUserShortcutType(getPrefContext(),
mComponentName.flattenToString());
mComponentName.flattenToString(), getDefaultShortcutTypes());
if (preference.isChecked()) {
final boolean isWarningRequired;
if (android.view.accessibility.Flags.cleanupAccessibilityWarningDialog()) {
@@ -476,6 +477,16 @@ public class ToggleAccessibilityServicePreferenceFragment extends
return TAG;
}
@Override
protected int getDefaultShortcutTypes() {
if (android.view.accessibility.Flags.a11yQsShortcut()) {
return getTileComponentName() == null ? super.getDefaultShortcutTypes()
: ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
}
return super.getDefaultShortcutTypes();
}
private void onAllowButtonFromEnableToggleClicked() {
handleConfirmServiceEnabled(/* confirmed= */ true);
if (serviceSupportsAccessibilityButton()) {
@@ -507,7 +518,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mShortcutPreference.setChecked(true);
final int shortcutTypes = retrieveUserShortcutType(getPrefContext(),
mComponentName.flattenToString());
mComponentName.flattenToString(), getDefaultShortcutTypes());
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName);
mIsDialogShown.set(false);

View File

@@ -54,6 +54,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
@@ -661,7 +662,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
int value = restoreOnConfigChangedValue();
if (value == NOT_SET) {
final int lastNonEmptyUserShortcutType = PreferredShortcuts.retrieveUserShortcutType(
getPrefContext(), mComponentName.flattenToString());
getPrefContext(), mComponentName.flattenToString(), getDefaultShortcutTypes());
value = mShortcutPreference.isChecked() ? lastNonEmptyUserShortcutType
: UserShortcutType.EMPTY;
}
@@ -710,8 +711,8 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
return context.getText(R.string.accessibility_shortcut_state_off);
}
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
mComponentName.flattenToString());
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(
context, mComponentName.flattenToString(), getDefaultShortcutTypes());
final List<CharSequence> list = new ArrayList<>();
if (android.view.accessibility.Flags.a11yQsShortcut()) {
@@ -811,7 +812,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
}
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
mComponentName.flattenToString());
mComponentName.flattenToString(), getDefaultShortcutTypes());
mShortcutPreference.setChecked(
AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes,
mComponentName));
@@ -829,7 +830,7 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
}
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
mComponentName.flattenToString());
mComponentName.flattenToString(), getDefaultShortcutTypes());
if (preference.isChecked()) {
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes,
mComponentName);
@@ -977,4 +978,13 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
boolean isAnySetupWizard() {
return WizardManagerHelper.isAnySetupWizard(getIntent());
}
/**
* Returns the default preferred shortcut types when the user doesn't have a preferred shortcut
* types
*/
@ShortcutConstants.UserShortcutType
protected int getDefaultShortcutTypes() {
return ShortcutConstants.UserShortcutType.SOFTWARE;
}
}

View File

@@ -39,6 +39,7 @@ import com.android.settings.Settings;
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal;
@@ -259,6 +260,7 @@ public class ActivityEmbeddingRulesController {
addActivityFilter(activityFilters, FingerprintEnrollIntroduction.class);
addActivityFilter(activityFilters, FingerprintEnrollIntroductionInternal.class);
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
addActivityFilter(activityFilters, FaceEnrollIntroduction.class);
addActivityFilter(activityFilters, RemoteAuthActivity.class);
addActivityFilter(activityFilters, RemoteAuthActivityInternal.class);

View File

@@ -71,7 +71,6 @@ import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.flags.Flags;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.display.DisplayDensityUtils;
@@ -252,12 +251,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
setContentView(layout);
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
if (Flags.udfpsEnrollCalibration()) {
mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
.getUdfpsEnrollCalibrator(getApplicationContext(), savedInstanceState,
getIntent());
}
} else if (mCanAssumeSfps) {
mSfpsEnrollmentFeature = FeatureFactory.getFeatureFactory()
.getFingerprintFeatureProvider().getSfpsEnrollmentFeature();
@@ -342,6 +335,15 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
final Configuration config = getApplicationContext().getResources().getConfiguration();
maybeHideSfpsText(config);
if (!mIsSetupWizard) {
mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
.getUdfpsEnrollCalibrator(getApplicationContext(), null, getIntent());
if (mCalibrator != null) {
mCalibrator.onWaitingPage(getLifecycle(),
getSupportFragmentManager(), null);
}
}
}
private void setHelpAnimation() {
@@ -371,11 +373,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_STATE_CANCELED, mIsCanceled);
outState.putInt(KEY_STATE_PREVIOUS_ROTATION, mPreviousRotation);
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
mCalibrator.onSaveInstanceState(outState);
}
}
}
private void restoreSavedState(Bundle savedInstanceState) {

View File

@@ -167,7 +167,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
.getUdfpsEnrollCalibrator(getApplicationContext(), savedInstanceState,
getIntent());
if (mCalibrator != null) {
mCalibrator.onFindSensorPage(
mCalibrator.onWaitingPage(
getLifecycle(),
getSupportFragmentManager(),
this::enableUdfpsLottieAndNextButton
@@ -296,7 +296,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
ret.putExtras(mCalibrator.getExtrasForNextIntent(true));
ret.putExtras(mCalibrator.getExtrasForNextIntent());
}
}
return ret;

View File

@@ -387,7 +387,7 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
}
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
intent.putExtras(mCalibrator.getExtrasForNextIntent(false));
intent.putExtras(mCalibrator.getExtrasForNextIntent());
}
}
intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,

View File

@@ -88,6 +88,7 @@ import com.android.settingslib.transition.SettingsTransitionHelper;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.TwoTargetPreference;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.util.DeviceHelper;
import java.util.ArrayList;
@@ -111,6 +112,9 @@ public class FingerprintSettings extends SubSettings {
private static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP;
private static final int RESULT_TIMEOUT = BiometricEnrollBase.RESULT_TIMEOUT;
@Nullable
private UdfpsEnrollCalibrator mCalibrator;
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
@@ -131,6 +135,13 @@ public class FingerprintSettings extends SubSettings {
setTitle(msg);
}
@Override
public void onResume() {
super.onResume();
mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
.getUdfpsEnrollCalibrator(getApplicationContext(), null, null);
}
/**
* @param context
* @return true if the Fingerprint hardware is detected.
@@ -800,6 +811,11 @@ public class FingerprintSettings extends SubSettings {
}
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
if (((FingerprintSettings) getActivity()).mCalibrator != null) {
intent.putExtras(
(((FingerprintSettings) getActivity()).mCalibrator)
.getExtrasForNextIntent());
}
startActivityForResult(intent, ADD_FINGERPRINT_REQUEST);
} else if (pref instanceof FingerprintPreference) {
FingerprintPreference fpref = (FingerprintPreference) pref;

View File

@@ -51,7 +51,7 @@ public class SetupFingerprintEnrollFindSensor extends FingerprintEnrollFindSenso
SetupWizardUtils.copySetupExtras(getIntent(), intent);
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
intent.putExtras(mCalibrator.getExtrasForNextIntent(true));
intent.putExtras(mCalibrator.getExtrasForNextIntent());
}
}
return intent;

View File

@@ -49,7 +49,7 @@ public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntrodu
SetupWizardUtils.copySetupExtras(getIntent(), intent);
if (Flags.udfpsEnrollCalibration()) {
if (mCalibrator != null) {
intent.putExtras(mCalibrator.getExtrasForNextIntent(false));
intent.putExtras(mCalibrator.getExtrasForNextIntent());
}
}
return intent;

View File

@@ -6,13 +6,14 @@ import androidx.lifecycle.Lifecycle
interface UdfpsEnrollCalibrator {
fun getExtrasForNextIntent(isEnrolling: Boolean): Bundle
fun getExtrasForNextIntent(): Bundle
fun onSaveInstanceState(outState: Bundle)
fun onFindSensorPage(
fun onWaitingPage(
lifecycle: Lifecycle,
fragmentManager: FragmentManager,
enableEnrollingRunnable: Runnable
enableEnrollingRunnable: Runnable?
)
}

View File

@@ -16,15 +16,19 @@
package com.android.settings.development;
import android.annotation.FlaggedApi;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecType;
/**
* Utility class for storing current Bluetooth A2DP profile values
*/
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/** Utility class for storing current Bluetooth A2DP profile values */
public class BluetoothA2dpConfigStore {
// init default values
private int mCodecType = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
private int mCodecTypeNative = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
@Nullable private BluetoothCodecType mCodecType = null;
private int mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
private int mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE;
private int mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
@@ -35,6 +39,10 @@ public class BluetoothA2dpConfigStore {
private long mCodecSpecific4Value;
public void setCodecType(int codecType) {
mCodecTypeNative = codecType;
}
public void setCodecType(@Nullable BluetoothCodecType codecType) {
mCodecType = codecType;
}
@@ -70,9 +78,26 @@ public class BluetoothA2dpConfigStore {
mCodecSpecific4Value = codecSpecific4Value;
}
/** Create codec config utilizing {@link BluetoothCodecConfig.SourceCodecType} */
public BluetoothCodecConfig createCodecConfig() {
return new BluetoothCodecConfig.Builder()
.setCodecType(mCodecType)
.setCodecType(mCodecTypeNative)
.setCodecPriority(mCodecPriority)
.setSampleRate(mSampleRate)
.setBitsPerSample(mBitsPerSample)
.setChannelMode(mChannelMode)
.setCodecSpecific1(mCodecSpecific1Value)
.setCodecSpecific2(mCodecSpecific2Value)
.setCodecSpecific3(mCodecSpecific3Value)
.setCodecSpecific4(mCodecSpecific4Value)
.build();
}
/** Create codec config utilizing {@link BluetoothCodecType} */
@FlaggedApi(Flags.FLAG_A2DP_OFFLOAD_CODEC_EXTENSIBILITY_SETTINGS)
public @NonNull BluetoothCodecConfig createCodecConfigFromCodecType() {
return new BluetoothCodecConfig.Builder()
.setExtendedCodecType(mCodecType)
.setCodecPriority(mCodecPriority)
.setSampleRate(mSampleRate)
.setBitsPerSample(mBitsPerSample)

View File

@@ -62,10 +62,12 @@ import com.android.settings.development.autofill.AutofillCategoryController;
import com.android.settings.development.autofill.AutofillLoggingLevelPreferenceController;
import com.android.settings.development.autofill.AutofillResetOptionsPreferenceController;
import com.android.settings.development.bluetooth.AbstractBluetoothDialogPreferenceController;
import com.android.settings.development.bluetooth.AbstractBluetoothListPreferenceController;
import com.android.settings.development.bluetooth.AbstractBluetoothPreferenceController;
import com.android.settings.development.bluetooth.BluetoothBitPerSampleDialogPreferenceController;
import com.android.settings.development.bluetooth.BluetoothChannelModeDialogPreferenceController;
import com.android.settings.development.bluetooth.BluetoothCodecDialogPreferenceController;
import com.android.settings.development.bluetooth.BluetoothCodecListPreferenceController;
import com.android.settings.development.bluetooth.BluetoothHDAudioPreferenceController;
import com.android.settings.development.bluetooth.BluetoothQualityDialogPreferenceController;
import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreferenceController;
@@ -744,6 +746,9 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
controllers.add(new AutofillResetOptionsPreferenceController(context));
controllers.add(new BluetoothCodecDialogPreferenceController(context, lifecycle,
bluetoothA2dpConfigStore, fragment));
controllers.add(
new BluetoothCodecListPreferenceController(
context, lifecycle, bluetoothA2dpConfigStore, fragment));
controllers.add(new BluetoothSampleRateDialogPreferenceController(context, lifecycle,
bluetoothA2dpConfigStore));
controllers.add(new BluetoothBitPerSampleDialogPreferenceController(context, lifecycle,
@@ -792,6 +797,9 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
((AbstractBluetoothDialogPreferenceController) controller).onHDAudioEnabled(
enabled);
}
if (controller instanceof AbstractBluetoothListPreferenceController) {
((AbstractBluetoothListPreferenceController) controller).onHDAudioEnabled(enabled);
}
}
}

View File

@@ -0,0 +1,268 @@
/*
* Copyright (C) 2023 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.development.bluetooth;
import static android.bluetooth.BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.development.BluetoothA2dpConfigStore;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.List;
/** Abstract class for Bluetooth A2DP config list controller in developer option. */
public abstract class AbstractBluetoothListPreferenceController
extends AbstractBluetoothPreferenceController
implements Preference.OnPreferenceChangeListener {
private static final String TAG = "AbstrBtListPrefCtrl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
protected static final int DEFAULT_VALUE_INT = 1000;
@Nullable protected ListPreference mListPreference;
protected String mDefaultEntry;
protected String mDefaultValue;
@Nullable protected final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
public AbstractBluetoothListPreferenceController(
@NonNull Context context,
@Nullable Lifecycle lifecycle,
@Nullable BluetoothA2dpConfigStore store) {
super(context, lifecycle, store);
mDefaultEntry = mContext.getString(R.string.bluetooth_audio_codec_default_selection);
mDefaultValue = String.valueOf(DEFAULT_VALUE_INT);
mBluetoothA2dpConfigStore = store;
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mListPreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean onPreferenceChange(@Nullable Preference preference, @NonNull Object newValue) {
if (DEBUG) {
Log.d(TAG, "onPreferenceChange: newValue=" + (String) newValue);
}
if (mListPreference == null) {
Log.e(TAG, "onPreferenceChange: List preference is null");
return false;
}
updateState(mListPreference);
return true;
}
@Override
public void updateState(@Nullable Preference preference) {
setupDefaultListPreference();
}
@Override
public void onBluetoothServiceConnected(@NonNull BluetoothA2dp bluetoothA2dp) {
super.onBluetoothServiceConnected(bluetoothA2dp);
initConfigStore();
}
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
if (DEBUG) {
Log.d(TAG, "onDeveloperOptionsSwitchDisabled");
}
if (mListPreference == null) {
Log.e(TAG, "onDeveloperOptionsSwitchDisabled: List preference is null");
return;
}
updateState(mListPreference);
}
/**
* Method to notify controller when the HD audio(optional codec) state is changed.
*
* @param enabled Is {@code true} when the setting is enabled.
*/
public void onHDAudioEnabled(boolean enabled) {}
/**
* Updates the new value to the {@link BluetoothA2dpConfigStore}.
*
* @param entryValue the new setting entry value
*/
protected abstract void writeConfigurationValues(String entryValue);
/**
* Gets the current bluetooth codec status.
*
* @return {@link BluetoothCodecStatus}.
*/
@Nullable
protected BluetoothCodecStatus getBluetoothCodecStatus() {
final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
if (bluetoothA2dp == null) {
Log.e(
TAG,
"getBluetoothCodecStatus: Unable to get codec status. Bluetooth A2dp is null.");
return null;
}
final BluetoothDevice activeDevice = getA2dpActiveDevice();
if (activeDevice == null) {
Log.e(TAG, "getBluetoothCodecStatus: Unable to get codec status. No active device.");
return null;
}
final BluetoothCodecStatus codecStatus = bluetoothA2dp.getCodecStatus(activeDevice);
if (codecStatus == null) {
Log.e(TAG, "getBluetoothCodecStatus: Codec status is null");
return null;
}
return codecStatus;
}
/**
* Gets the current bluetooth codec config.
*
* @return {@link BluetoothCodecConfig}.
*/
@Nullable
protected BluetoothCodecConfig getCurrentCodecConfig() {
final BluetoothCodecStatus codecStatus = getBluetoothCodecStatus();
if (codecStatus == null) {
Log.e(
TAG,
"getCurrentCodecConfig: Unable to get current codec config. Codec status is"
+ " null");
return null;
}
return codecStatus.getCodecConfig();
}
/**
* Sets the {@link ListPreference}. This method adds the default entry and the entry value
* automatically.
*
* @param entries list of String entries for the {@link ListPreference}.
* @param entryValues list of String entry values for the {@link ListPreference}.
* @param selectedEntry currently selected entry.
* @param selectedValue currently selected entry value.
*/
protected void setupListPreference(
List<String> entries,
List<String> entryValues,
String selectedEntry,
String selectedValue) {
if (entries.size() != entryValues.size()) {
Log.e(
TAG,
("setupListPreference: size of entries: " + entries.size())
+ (", size of entryValues" + entryValues.size()));
setupDefaultListPreference();
return;
}
if (entries.isEmpty() || entryValues.isEmpty()) {
Log.e(TAG, "setupListPreference: entries or entryValues empty");
setupDefaultListPreference();
return;
}
entries.add(0, mDefaultEntry);
entryValues.add(0, mDefaultValue);
if (mListPreference == null) {
Log.e(TAG, "setupListPreference: List preference is null");
return;
}
mListPreference.setEntries(entries.toArray(new String[entries.size()]));
mListPreference.setEntryValues(entryValues.toArray(new String[entryValues.size()]));
mListPreference.setValue(selectedValue);
mListPreference.setSummary(selectedEntry);
}
/**
* Check HD Audio enabled.
*
* @return true if HD Audio is enabled.
*/
protected boolean isHDAudioEnabled() {
final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
if (bluetoothA2dp == null) {
Log.e(TAG, "isHDAudioEnabled: Unable to get codec status. BluetoothA2dp is null.");
return false;
}
BluetoothDevice activeDevice = getA2dpActiveDevice();
if (activeDevice == null) {
Log.e(TAG, "isHDAudioEnabled: Unable to get codec status. No active device.");
return false;
}
return (bluetoothA2dp.isOptionalCodecsEnabled(activeDevice)
== BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
}
private void setupDefaultListPreference() {
if (DEBUG) {
Log.d(
TAG,
"setupDefaultListPreference: mDefaultEntry="
+ mDefaultEntry
+ ", mDefaultValue="
+ mDefaultValue);
}
if (mListPreference == null) {
Log.e(TAG, "setupListPreference: List preference is null");
return;
}
mListPreference.setEntries(new String[] {mDefaultEntry});
mListPreference.setEntryValues(new String[] {mDefaultValue});
mListPreference.setValue(mDefaultValue);
mListPreference.setSummary(mDefaultEntry);
}
private void initConfigStore() {
final BluetoothCodecConfig config = getCurrentCodecConfig();
if (config == null) {
Log.e(TAG, "initConfigStore: Current codec config is null.");
return;
}
if (mBluetoothA2dpConfigStore == null) {
Log.e(TAG, "initConfigStore: Bluetooth A2dp Config Store is null.");
return;
}
mBluetoothA2dpConfigStore.setCodecType(config.getExtendedCodecType());
mBluetoothA2dpConfigStore.setSampleRate(config.getSampleRate());
mBluetoothA2dpConfigStore.setBitsPerSample(config.getBitsPerSample());
mBluetoothA2dpConfigStore.setChannelMode(config.getChannelMode());
mBluetoothA2dpConfigStore.setCodecPriority(CODEC_PRIORITY_HIGHEST);
mBluetoothA2dpConfigStore.setCodecSpecific1Value(config.getCodecSpecific1());
}
}

View File

@@ -23,6 +23,7 @@ import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.core.PreferenceControllerMixin;
@@ -42,13 +43,15 @@ public abstract class AbstractBluetoothPreferenceController extends
DeveloperOptionsPreferenceController implements BluetoothServiceConnectionListener,
LifecycleObserver, OnDestroy, PreferenceControllerMixin {
protected volatile BluetoothA2dp mBluetoothA2dp;
@Nullable protected volatile BluetoothA2dp mBluetoothA2dp;
@VisibleForTesting
BluetoothAdapter mBluetoothAdapter;
public AbstractBluetoothPreferenceController(Context context, Lifecycle lifecycle,
BluetoothA2dpConfigStore store) {
public AbstractBluetoothPreferenceController(
@Nullable Context context,
@Nullable Lifecycle lifecycle,
@Nullable BluetoothA2dpConfigStore store) {
super(context);
if (lifecycle != null) {
lifecycle.addObserver(this);

View File

@@ -26,6 +26,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.development.BluetoothA2dpConfigStore;
import com.android.settings.development.Flags;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
@@ -49,6 +50,11 @@ public class BluetoothCodecDialogPreferenceController extends
mCallback = callback;
}
@Override
public boolean isAvailable() {
return !Flags.a2dpOffloadCodecExtensibilitySettings();
}
@Override
public String getPreferenceKey() {
return KEY;

View File

@@ -0,0 +1,265 @@
/*
* Copyright (C) 2023 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.development.bluetooth;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothCodecType;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.development.BluetoothA2dpConfigStore;
import com.android.settings.development.Flags;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/** List preference controller to set the Bluetooth A2DP codec */
public class BluetoothCodecListPreferenceController
extends AbstractBluetoothListPreferenceController {
private static final String KEY = "bluetooth_audio_codec_settings_list";
private static final String TAG = "BtExtCodecCtr";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@Nullable private final Callback mCallback;
public BluetoothCodecListPreferenceController(
@NonNull Context context,
@Nullable Lifecycle lifecycle,
@Nullable BluetoothA2dpConfigStore store,
@Nullable Callback callback) {
super(context, lifecycle, store);
mCallback = callback;
}
@Override
public boolean isAvailable() {
boolean available = Flags.a2dpOffloadCodecExtensibilitySettings();
if (DEBUG) {
Log.d(TAG, "isAvailable: " + available);
}
return available;
}
@Override
public @NonNull String getPreferenceKey() {
return KEY;
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mListPreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean onPreferenceChange(@Nullable Preference preference, @NonNull Object newValue) {
if (DEBUG) {
Log.d(TAG, "onPreferenceChange: newValue=" + (String) newValue);
}
final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
if (bluetoothA2dp == null) {
Log.e(TAG, "onPreferenceChange: bluetoothA2dp is null");
return false;
}
writeConfigurationValues((String) newValue);
if (mBluetoothA2dpConfigStore == null) {
Log.e(TAG, "onPreferenceChange: Bluetooth A2dp Config Store is null");
return false;
}
BluetoothCodecConfig codecConfig;
if (Flags.a2dpOffloadCodecExtensibilitySettings()) {
codecConfig = mBluetoothA2dpConfigStore.createCodecConfigFromCodecType();
} else {
codecConfig = mBluetoothA2dpConfigStore.createCodecConfig();
}
final BluetoothDevice activeDevice = getA2dpActiveDevice();
if (activeDevice == null) {
Log.e(TAG, "onPreferenceChange: active device is null");
return false;
}
if (DEBUG) {
Log.d(TAG, "onPreferenceChange: setCodecConfigPreference: " + codecConfig.toString());
}
bluetoothA2dp.setCodecConfigPreference(activeDevice, codecConfig);
if (mCallback != null) {
mCallback.onBluetoothCodecChanged();
}
return true;
}
@Override
public void updateState(@Nullable Preference preference) {
super.updateState(preference);
final List<String> codecIds = new ArrayList<>();
final List<String> labels = new ArrayList<>();
String selectedCodecId = mDefaultValue;
String selectedLabel = mDefaultEntry;
if (isHDAudioEnabled()) {
final BluetoothCodecStatus codecStatus = getBluetoothCodecStatus();
if (codecStatus == null) {
Log.e(TAG, "updateState: Bluetooth Codec Status is null");
return;
}
final BluetoothCodecConfig currentCodecConfig = codecStatus.getCodecConfig();
if (currentCodecConfig == null) {
Log.e(TAG, "updateState: currentCodecConfig is null");
return;
}
final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
if (bluetoothA2dp == null) {
Log.e(TAG, "updateState: bluetoothA2dp is null");
return;
}
final Collection<BluetoothCodecType> codecTypes =
bluetoothA2dp.getSupportedCodecTypes();
for (BluetoothCodecType codecType : codecTypes) {
labels.add(codecType.getCodecName());
codecIds.add(String.valueOf(codecType.getCodecId()));
if (currentCodecConfig != null
&& currentCodecConfig.getExtendedCodecType().equals(codecType)) {
selectedCodecId = codecIds.get(codecIds.size() - 1);
selectedLabel = labels.get(labels.size() - 1);
if (DEBUG) {
Log.d(
TAG,
"updateState: Current config: "
+ selectedLabel
+ ", id: "
+ selectedCodecId);
}
}
}
setupListPreference(labels, codecIds, selectedLabel, selectedCodecId);
}
}
@Override
public void onHDAudioEnabled(boolean enabled) {
if (DEBUG) {
Log.d(TAG, "onHDAudioEnabled: enabled=" + enabled);
}
if (mListPreference == null) {
Log.e(TAG, "onHDAudioEnabled: List preference is null");
return;
}
mListPreference.setEnabled(enabled);
}
@Override
protected void writeConfigurationValues(String entryValue) {
long codecIdValue = getCodecIdFromEntryValue(entryValue);
BluetoothCodecType selectedCodecType = null;
BluetoothCodecConfig selectedCodecConfig = null;
final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
if (bluetoothA2dp == null) {
Log.e(TAG, "writeConfigurationValues: bluetoothA2dp is null");
return;
}
final Collection<BluetoothCodecType> codecTypes = bluetoothA2dp.getSupportedCodecTypes();
for (BluetoothCodecType codecType : codecTypes) {
if (codecType.getCodecId() == codecIdValue) {
selectedCodecType = codecType;
}
}
if (selectedCodecType == null) {
Log.e(
TAG,
"writeConfigurationValues: No selectable codec ID: "
+ codecIdValue
+ " found. Unable to change codec");
return;
}
if (DEBUG) {
Log.d(TAG, "writeConfigurationValues: Selected codec: " + selectedCodecType.toString());
}
final BluetoothCodecStatus codecStatus = getBluetoothCodecStatus();
if (codecStatus == null) {
Log.e(TAG, "writeConfigurationValues: Bluetooth Codec Status is null");
return;
}
final List<BluetoothCodecConfig> codecConfigs =
codecStatus.getCodecsSelectableCapabilities();
for (BluetoothCodecConfig config : codecConfigs) {
BluetoothCodecType codecType = config.getExtendedCodecType();
if (codecType == null) {
Log.e(TAG, "codec type for config:" + config + " is null");
}
if (codecType != null && codecType.equals(selectedCodecType)) {
selectedCodecConfig = config;
}
}
if (selectedCodecConfig == null) {
Log.e(
TAG,
"writeConfigurationValues: No selectable codec config for codec: "
+ selectedCodecType.toString());
return;
}
if (mBluetoothA2dpConfigStore == null) {
Log.e(TAG, "writeConfigurationValues: Bluetooth A2dp Config Store is null");
return;
}
mBluetoothA2dpConfigStore.setCodecType(selectedCodecType);
mBluetoothA2dpConfigStore.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST);
mBluetoothA2dpConfigStore.setSampleRate(
AbstractBluetoothDialogPreferenceController.getHighestSampleRate(
selectedCodecConfig));
mBluetoothA2dpConfigStore.setBitsPerSample(
AbstractBluetoothDialogPreferenceController.getHighestBitsPerSample(
selectedCodecConfig));
mBluetoothA2dpConfigStore.setChannelMode(
AbstractBluetoothDialogPreferenceController.getHighestChannelMode(
selectedCodecConfig));
}
private long getCodecIdFromEntryValue(String entryValue) {
long codecType = BluetoothCodecType.CODEC_ID_SBC;
if (entryValue.isEmpty() || Long.valueOf(entryValue) == DEFAULT_VALUE_INT) {
return codecType;
}
return Long.valueOf(entryValue);
}
}

View File

@@ -31,6 +31,8 @@ import android.hardware.display.DisplayManager.DisplayListener;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.os.UserManager;
import android.provider.Settings.System;
import android.text.TextUtils;
@@ -116,6 +118,10 @@ public class BrightnessLevelPreferenceController extends AbstractPreferenceContr
@Override
public void updateState(Preference preference) {
if (preference.isEnabled() && UserManager.get(mContext).hasBaseUserRestriction(
UserManager.DISALLOW_CONFIG_BRIGHTNESS, Process.myUserHandle())) {
preference.setEnabled(false);
}
updatedSummary(preference);
}

View File

@@ -23,6 +23,7 @@ import static com.android.settings.display.ScreenTimeoutSettings.FALLBACK_SCREEN
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -67,9 +68,13 @@ public class ScreenTimeoutPreferenceController extends BasePreferenceController
.getString(DISABLED_BY_IT_ADMIN_TITLE,
() -> mContext.getString(R.string.disabled_by_policy_title)));
((RestrictedPreference) preference).setDisabledByAdmin(admin);
} else {
preference.setSummary(getTimeoutSummary(maxTimeout));
return;
}
if (UserManager.get(mContext).hasBaseUserRestriction(
UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, Process.myUserHandle())) {
preference.setEnabled(false);
}
preference.setSummary(getTimeoutSummary(maxTimeout));
}
private CharSequence getTimeoutSummary(long maxTimeout) {

View File

@@ -927,34 +927,45 @@ public class ChooseLockGeneric extends SettingsActivity {
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)) {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
if (hasFingerprints && hasFace) {
return R.string.unlock_disable_frp_warning_content_pattern_face_fingerprint;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_face_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_pattern_face_fingerprint;
} else if (hasFingerprints) {
return R.string.unlock_disable_frp_warning_content_pattern_fingerprint;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_pattern_fingerprint;
} else if (hasFace) {
return R.string.unlock_disable_frp_warning_content_pattern_face;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_face_authbound_keys
: R.string.unlock_disable_frp_warning_content_pattern_face;
} else {
return R.string.unlock_disable_frp_warning_content_pattern;
return hasAppsWithAuthBoundKeys
? R.string.unlock_disable_frp_warning_content_authbound_keys
: R.string.unlock_disable_frp_warning_content_pattern;
}
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
if (hasFingerprints && hasFace) {
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_pin_face_fingerprint_authbound_keys
R.string.unlock_disable_frp_warning_content_face_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_pin_face_fingerprint;
} else if (hasFingerprints) {
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_pin_fingerprint_authbound_keys
R.string.unlock_disable_frp_warning_content_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_pin_fingerprint;
} else if (hasFace) {
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_pin_face_authbound_keys
R.string.unlock_disable_frp_warning_content_face_authbound_keys
: R.string.unlock_disable_frp_warning_content_pin_face;
} else {
return hasAppsWithAuthBoundKeys
? R.string.unlock_disable_frp_warning_content_pin_authbound_keys
? R.string.unlock_disable_frp_warning_content_authbound_keys
: R.string.unlock_disable_frp_warning_content_pin;
}
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
@@ -962,24 +973,45 @@ public class ChooseLockGeneric extends SettingsActivity {
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
if (hasFingerprints && hasFace) {
return R.string
.unlock_disable_frp_warning_content_password_face_fingerprint;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_face_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_password_face_fingerprint;
} else if (hasFingerprints) {
return R.string.unlock_disable_frp_warning_content_password_fingerprint;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_password_fingerprint;
} else if (hasFace) {
return R.string.unlock_disable_frp_warning_content_password_face;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_face_authbound_keys
: R.string.unlock_disable_frp_warning_content_password_face;
} else {
return R.string.unlock_disable_frp_warning_content_password;
return hasAppsWithAuthBoundKeys
? R.string.unlock_disable_frp_warning_content_authbound_keys
: R.string.unlock_disable_frp_warning_content_password;
}
default:
if (hasFingerprints && hasFace) {
return R.string.unlock_disable_frp_warning_content_unknown_face_fingerprint;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_face_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_unknown_face_fingerprint;
} else if (hasFingerprints) {
return R.string.unlock_disable_frp_warning_content_unknown_fingerprint;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_fingerprint_authbound_keys
: R.string.unlock_disable_frp_warning_content_unknown_fingerprint;
} else if (hasFace) {
return R.string.unlock_disable_frp_warning_content_unknown_face;
return hasAppsWithAuthBoundKeys
?
R.string.unlock_disable_frp_warning_content_face_authbound_keys
: R.string.unlock_disable_frp_warning_content_unknown_face;
} else {
return R.string.unlock_disable_frp_warning_content_unknown;
return hasAppsWithAuthBoundKeys
? R.string.unlock_disable_frp_warning_content_authbound_keys
: R.string.unlock_disable_frp_warning_content_unknown;
}
}
}

View File

@@ -34,10 +34,10 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.quicksettings.TileService;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;
@@ -47,6 +47,7 @@ import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
@@ -68,13 +69,13 @@ import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.shadows.ShadowPackageManager;
import java.util.List;
import java.util.Set;
/** Tests for {@link ToggleAccessibilityServicePreferenceFragment} */
@RunWith(RobolectricTestRunner.class)
public class ToggleAccessibilityServicePreferenceFragmentTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example";
private static final String PLACEHOLDER_PACKAGE_NAME2 = "com.placeholder.example2";
@@ -236,7 +237,7 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@EnableFlags(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void enableService_warningRequired_showWarning() throws Throwable {
setupServiceWarningRequired(true);
mFragment.mToggleServiceSwitchPreference =
@@ -249,7 +250,7 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@EnableFlags(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void enableService_warningNotRequired_dontShowWarning() throws Throwable {
final AccessibilityServiceInfo info = setupServiceWarningRequired(false);
mFragment.mToggleServiceSwitchPreference =
@@ -263,7 +264,7 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@EnableFlags(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void toggleShortcutPreference_warningRequired_showWarning() throws Throwable {
setupServiceWarningRequired(true);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null);
@@ -277,7 +278,7 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@EnableFlags(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void toggleShortcutPreference_warningNotRequired_dontShowWarning() throws Throwable {
setupServiceWarningRequired(false);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null);
@@ -291,7 +292,7 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@EnableFlags(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
public void clickShortcutSettingsPreference_warningRequired_showWarning() throws Throwable {
setupServiceWarningRequired(true);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null);
@@ -303,8 +304,8 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@RequiresFlagsDisabled(
@EnableFlags(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@DisableFlags(
com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN)
public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_showDialog()
throws Throwable {
@@ -318,7 +319,7 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
}
@Test
@RequiresFlagsEnabled({Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG,
@EnableFlags({Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG,
com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN})
public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_launchActivity()
throws Throwable {
@@ -334,6 +335,155 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
.isEqualTo(EditShortcutsPreferenceFragment.class.getName());
}
@Test
public void getDefaultShortcutTypes_noAssociatedTile_softwareTypeIsDefault() {
PreferredShortcuts.clearPreferredShortcuts(mContext);
when(mFragment.getTileComponentName()).thenReturn(null);
assertThat(mFragment.getDefaultShortcutTypes())
.isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE);
}
@Test
@EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void getDefaultShortcutTypes_hasAssociatedTile_qsTypeIsDefault() {
PreferredShortcuts.clearPreferredShortcuts(mContext);
when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
assertThat(mFragment.getDefaultShortcutTypes())
.isEqualTo(ShortcutConstants.UserShortcutType.QUICK_SETTINGS);
}
@Test
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void getDefaultShortcutTypes_hasAssociatedTile_softwareTypeIsDefault() {
PreferredShortcuts.clearPreferredShortcuts(mContext);
when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
assertThat(mFragment.getDefaultShortcutTypes())
.isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE);
}
@Test
@EnableFlags({Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG, Flags.FLAG_A11Y_QS_SHORTCUT})
public void toggleShortcutPreference_noUserPreferredShortcut_hasQsTile_enableQsShortcut()
throws Throwable {
PreferredShortcuts.clearPreferredShortcuts(mContext);
setupServiceWarningRequired(false);
when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
mFragment.mShortcutPreference.setChecked(true);
mFragment.onToggleClicked(mFragment.mShortcutPreference);
verify(mMockAccessibilityManager)
.enableShortcutsForTargets(true,
ShortcutConstants.UserShortcutType.QUICK_SETTINGS,
Set.of(mFragment.mComponentName.flattenToString()), mContext.getUserId());
}
@Test
@EnableFlags({Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG, Flags.FLAG_A11Y_QS_SHORTCUT})
public void toggleShortcutPreference_noUserPreferredShortcut_noQsTile_enableSoftwareShortcut()
throws Throwable {
PreferredShortcuts.clearPreferredShortcuts(mContext);
setupServiceWarningRequired(false);
when(mFragment.getTileComponentName()).thenReturn(null);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
mFragment.mShortcutPreference.setChecked(true);
mFragment.onToggleClicked(mFragment.mShortcutPreference);
verify(mMockAccessibilityManager)
.enableShortcutsForTargets(true,
ShortcutConstants.UserShortcutType.SOFTWARE,
Set.of(mFragment.mComponentName.flattenToString()), mContext.getUserId());
}
@Test
@EnableFlags(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void toggleShortcutPreference_noUserPreferredShortcut_hasQsTile_flagOff_enableSoftwareShortcut()
throws Throwable {
PreferredShortcuts.clearPreferredShortcuts(mContext);
setupServiceWarningRequired(false);
when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
mFragment.mShortcutPreference.setChecked(true);
mFragment.onToggleClicked(mFragment.mShortcutPreference);
assertThat(
Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS))
.contains(mFragment.mComponentName.flattenToString());
}
@Test
@EnableFlags(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG)
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void toggleShortcutPreference_noUserPreferredShortcut_noQsTile_flagOff_enableSoftwareShortcut()
throws Throwable {
PreferredShortcuts.clearPreferredShortcuts(mContext);
setupServiceWarningRequired(false);
when(mFragment.getTileComponentName()).thenReturn(null);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
mFragment.mShortcutPreference.setChecked(true);
mFragment.onToggleClicked(mFragment.mShortcutPreference);
assertThat(
Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS))
.contains(mFragment.mComponentName.flattenToString());
}
@Test
@EnableFlags({Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG, Flags.FLAG_A11Y_QS_SHORTCUT})
public void toggleShortcutPreference_userPreferVolumeKeysShortcut_noQsTile_enableVolumeKeysShortcut()
throws Throwable {
setupServiceWarningRequired(false);
String componentName = mFragment.mComponentName.flattenToString();
PreferredShortcuts.saveUserShortcutType(
mContext,
new PreferredShortcut(componentName, ShortcutConstants.UserShortcutType.HARDWARE));
when(mFragment.getTileComponentName()).thenReturn(null);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
mFragment.mShortcutPreference.setChecked(true);
mFragment.onToggleClicked(mFragment.mShortcutPreference);
verify(mMockAccessibilityManager)
.enableShortcutsForTargets(
true,
ShortcutConstants.UserShortcutType.HARDWARE,
Set.of(componentName),
mContext.getUserId());
}
@Test
@EnableFlags({Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG, Flags.FLAG_A11Y_QS_SHORTCUT})
public void toggleShortcutPreference_userPreferVolumeKeysShortcut_hasQsTile_enableVolumeKeysShortcut()
throws Throwable {
setupServiceWarningRequired(false);
String componentName = mFragment.mComponentName.flattenToString();
PreferredShortcuts.saveUserShortcutType(
mContext,
new PreferredShortcut(componentName, ShortcutConstants.UserShortcutType.HARDWARE));
when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
mFragment.mShortcutPreference.setChecked(true);
mFragment.onToggleClicked(mFragment.mShortcutPreference);
verify(mMockAccessibilityManager)
.enableShortcutsForTargets(
true,
ShortcutConstants.UserShortcutType.HARDWARE,
Set.of(componentName),
mContext.getUserId());
}
private void setupTileService(String packageName, String name, String tileName) {
final Intent tileProbe = new Intent(TileService.ACTION_QS_TILE);
final ResolveInfo info = new ResolveInfo();

View File

@@ -0,0 +1,240 @@
/*
* Copyright (C) 2023 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.development.bluetooth;
import static android.bluetooth.BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.ListPreference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.development.BluetoothA2dpConfigStore;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class AbstractBluetoothListPreferenceControllerTest {
private static final String DEVICE_ADDRESS = "00:11:22:33:44:55";
private static String DEFAULT_ENTRY;
private static final String DEFAULT_ENTRY_VALUE = "1000";
@Mock private BluetoothA2dp mBluetoothA2dp;
@Mock private BluetoothAdapter mBluetoothAdapter;
@Mock private PreferenceScreen mScreen;
private AbstractBluetoothListPreferenceController mController;
private ListPreference mPreference;
private BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
private BluetoothCodecStatus mCodecStatus;
private BluetoothCodecConfig mCodecConfigAAC;
private BluetoothCodecConfig mCodecConfigSBC;
private BluetoothCodecConfig[] mCodecConfigs = new BluetoothCodecConfig[2];
private BluetoothDevice mActiveDevice;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mBluetoothA2dpConfigStore = spy(new BluetoothA2dpConfigStore());
mActiveDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(DEVICE_ADDRESS);
mController =
spy(
new AbstractBluetoothListPreferenceControllerImpl(
mContext, mLifecycle, mBluetoothA2dpConfigStore));
mController.mBluetoothAdapter = mBluetoothAdapter;
mPreference = spy(new ListPreference(mContext));
mCodecConfigAAC =
new BluetoothCodecConfig.Builder()
.setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC)
.build();
mCodecConfigSBC =
new BluetoothCodecConfig.Builder()
.setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC)
.build();
mCodecConfigs[0] = mCodecConfigAAC;
mCodecConfigs[1] = mCodecConfigSBC;
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);
when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.A2DP)))
.thenReturn(Arrays.asList(mActiveDevice));
DEFAULT_ENTRY = mContext.getString(R.string.bluetooth_audio_codec_default_selection);
}
private void verifySetupDefaultListPreference() {
List<String> entries = new ArrayList<>(1);
entries.add(DEFAULT_ENTRY);
List<String> entryValues = new ArrayList<>(1);
entryValues.add(DEFAULT_ENTRY_VALUE);
verify(mPreference).setEntries(entries.toArray(new String[entries.size()]));
verify(mPreference).setEntryValues(entryValues.toArray(new String[entryValues.size()]));
verify(mPreference).setValue(DEFAULT_ENTRY_VALUE);
verify(mPreference).setSummary(DEFAULT_ENTRY);
}
@Test
public void onPreferenceChange_shouldSetupDefaultListPreference() {
mController.onPreferenceChange(mPreference, "" /* new value */);
verifySetupDefaultListPreference();
}
@Test
public void setupListPreference_wrongSize_shouldSetupDefaultListPreference() {
List<String> entries = new ArrayList<>(1);
entries.add(DEFAULT_ENTRY);
List<String> entryValues = new ArrayList<>(2);
entryValues.add(DEFAULT_ENTRY_VALUE);
entryValues.add(DEFAULT_ENTRY_VALUE);
mController.setupListPreference(entries, entryValues, "", "");
verifySetupDefaultListPreference();
}
@Test
public void setupListPreference_listEmpty_shouldSetupDefaultListPreference() {
List<String> entries = new ArrayList<>(1);
entries.add(DEFAULT_ENTRY);
List<String> entryValues = new ArrayList<>();
mController.setupListPreference(entries, entryValues, "", "");
verifySetupDefaultListPreference();
}
@Test
public void getBluetoothCodecStatus_errorChecking() {
mController.onBluetoothServiceConnected(null);
assertThat(mController.getBluetoothCodecStatus()).isNull();
mController.onBluetoothServiceConnected(mBluetoothA2dp);
when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(null);
assertThat(mController.getBluetoothCodecStatus()).isNull();
}
@Test
public void getCurrentCodecConfig_errorChecking() {
mController.onBluetoothServiceConnected(null);
assertThat(mController.getCurrentCodecConfig()).isNull();
mController.onBluetoothServiceConnected(mBluetoothA2dp);
when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(null);
assertThat(mController.getCurrentCodecConfig()).isNull();
}
@Test
public void getCurrentCodecConfig_verifyConfig() {
mCodecStatus = new BluetoothCodecStatus.Builder().setCodecConfig(mCodecConfigAAC).build();
when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(mCodecStatus);
mController.onBluetoothServiceConnected(mBluetoothA2dp);
assertThat(mController.getCurrentCodecConfig()).isEqualTo(mCodecConfigAAC);
}
@Test
public void isHDAudioEnabled_errorChecking() {
mController.onBluetoothServiceConnected(null);
assertFalse(mController.isHDAudioEnabled());
mController.onBluetoothServiceConnected(mBluetoothA2dp);
when(mBluetoothA2dp.isOptionalCodecsEnabled(mActiveDevice))
.thenReturn(BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED);
assertFalse(mController.isHDAudioEnabled());
}
@Test
public void isHDAudioEnabled_verifyEnabled() {
mController.onBluetoothServiceConnected(mBluetoothA2dp);
when(mBluetoothA2dp.isOptionalCodecsEnabled(mActiveDevice))
.thenReturn(BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
assertTrue(mController.isHDAudioEnabled());
}
@Test
public void onBluetoothServiceConnected_verifyBluetoothA2dpConfigStore() {
mCodecStatus =
new BluetoothCodecStatus.Builder()
.setCodecConfig(mCodecConfigAAC)
.setCodecsSelectableCapabilities(Arrays.asList(mCodecConfigs))
.build();
when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(mCodecStatus);
mController.onBluetoothServiceConnected(mBluetoothA2dp);
verify(mBluetoothA2dpConfigStore).setCodecType(mCodecConfigAAC.getExtendedCodecType());
verify(mBluetoothA2dpConfigStore).setSampleRate(mCodecConfigAAC.getSampleRate());
verify(mBluetoothA2dpConfigStore).setBitsPerSample(mCodecConfigAAC.getBitsPerSample());
verify(mBluetoothA2dpConfigStore).setChannelMode(mCodecConfigAAC.getChannelMode());
verify(mBluetoothA2dpConfigStore).setCodecPriority(CODEC_PRIORITY_HIGHEST);
verify(mBluetoothA2dpConfigStore)
.setCodecSpecific1Value(mCodecConfigAAC.getCodecSpecific1());
}
private static class AbstractBluetoothListPreferenceControllerImpl
extends AbstractBluetoothListPreferenceController {
private AbstractBluetoothListPreferenceControllerImpl(
Context context, Lifecycle lifecycle, BluetoothA2dpConfigStore store) {
super(context, lifecycle, store);
}
@Override
public String getPreferenceKey() {
return "KEY";
}
@Override
protected void writeConfigurationValues(String entryValue) {}
}
}

View File

@@ -0,0 +1,267 @@
/*
* Copyright (C) 2023 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.development.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothCodecType;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.ListPreference;
import androidx.preference.PreferenceScreen;
import com.android.settings.development.BluetoothA2dpConfigStore;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class BluetoothCodecListPreferenceControllerTest {
private static final String DEVICE_ADDRESS = "00:11:22:33:44:55";
@Mock private BluetoothA2dp mBluetoothA2dp;
@Mock private BluetoothAdapter mBluetoothAdapter;
@Mock private PreferenceScreen mScreen;
@Mock private AbstractBluetoothPreferenceController.Callback mCallback;
private BluetoothCodecListPreferenceController mController;
private ListPreference mPreference;
private BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
private BluetoothCodecStatus mCodecStatus;
private BluetoothCodecType mCodecTypeAAC;
private BluetoothCodecType mCodecTypeSBC;
private BluetoothCodecType mCodecTypeAPTX;
private BluetoothCodecType mCodecTypeLDAC;
private BluetoothCodecType mCodecTypeOPUS;
private List<BluetoothCodecType> mCodecTypes;
private BluetoothCodecConfig mCodecConfigAAC;
private BluetoothCodecConfig mCodecConfigSBC;
private BluetoothCodecConfig mCodecConfigAPTX;
private BluetoothCodecConfig mCodecConfigAPTXHD;
private BluetoothCodecConfig mCodecConfigLDAC;
private BluetoothCodecConfig mCodecConfigOPUS;
private List<BluetoothCodecConfig> mCodecConfigs;
private BluetoothDevice mActiveDevice;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mBluetoothA2dpConfigStore = spy(new BluetoothA2dpConfigStore());
mActiveDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(DEVICE_ADDRESS);
mController =
new BluetoothCodecListPreferenceController(
mContext, mLifecycle, mBluetoothA2dpConfigStore, mCallback);
mController.mBluetoothAdapter = mBluetoothAdapter;
mPreference = new ListPreference(mContext);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);
mCodecTypeAAC =
BluetoothCodecType.createFromType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC);
mCodecTypeSBC =
BluetoothCodecType.createFromType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC);
mCodecTypeAPTX =
BluetoothCodecType.createFromType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX);
mCodecTypeLDAC =
BluetoothCodecType.createFromType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC);
mCodecTypeOPUS =
BluetoothCodecType.createFromType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS);
mCodecTypes = new ArrayList<>();
mCodecTypes.addAll(
Arrays.asList(
mCodecTypeSBC,
mCodecTypeAAC,
mCodecTypeAPTX,
mCodecTypeLDAC,
mCodecTypeOPUS));
mCodecConfigSBC =
new BluetoothCodecConfig.Builder()
.setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC)
.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)
.setSampleRate(
BluetoothCodecConfig.SAMPLE_RATE_96000
| BluetoothCodecConfig.SAMPLE_RATE_176400)
.setBitsPerSample(BluetoothCodecConfig.BITS_PER_SAMPLE_32)
.setChannelMode(
BluetoothCodecConfig.CHANNEL_MODE_MONO
| BluetoothCodecConfig.CHANNEL_MODE_STEREO)
.build();
mCodecConfigAAC =
new BluetoothCodecConfig.Builder()
.setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC)
.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)
.setSampleRate(
BluetoothCodecConfig.SAMPLE_RATE_48000
| BluetoothCodecConfig.SAMPLE_RATE_88200)
.setBitsPerSample(
BluetoothCodecConfig.BITS_PER_SAMPLE_16
| BluetoothCodecConfig.BITS_PER_SAMPLE_24)
.setChannelMode(BluetoothCodecConfig.CHANNEL_MODE_STEREO)
.build();
mCodecConfigAPTX =
new BluetoothCodecConfig.Builder()
.setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX)
.build();
mCodecConfigAPTXHD =
new BluetoothCodecConfig.Builder()
.setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD)
.build();
mCodecConfigLDAC =
new BluetoothCodecConfig.Builder()
.setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC)
.build();
mCodecConfigOPUS =
new BluetoothCodecConfig.Builder()
.setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS)
.build();
mCodecConfigs = new ArrayList<>();
mCodecConfigs.addAll(
Arrays.asList(
mCodecConfigOPUS,
mCodecConfigAAC,
mCodecConfigSBC,
mCodecConfigAPTX,
mCodecConfigAPTXHD,
mCodecConfigLDAC));
when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.A2DP)))
.thenReturn(Arrays.asList(mActiveDevice));
when(mBluetoothA2dp.getSupportedCodecTypes()).thenReturn(mCodecTypes);
}
@Test
public void writeConfigurationValues_selectDefault() {
mCodecStatus =
new BluetoothCodecStatus.Builder()
.setCodecConfig(mCodecConfigSBC)
.setCodecsSelectableCapabilities(mCodecConfigs)
.build();
when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(mCodecStatus);
when(mBluetoothA2dp.isOptionalCodecsEnabled(mActiveDevice))
.thenReturn(BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
mController.onBluetoothServiceConnected(mBluetoothA2dp);
mController.writeConfigurationValues(String.valueOf(mController.DEFAULT_VALUE_INT));
verify(mBluetoothA2dpConfigStore, times(2)).setCodecType(mCodecTypeSBC);
}
@Test
public void writeConfigurationValues_checkCodec() {
mCodecStatus =
new BluetoothCodecStatus.Builder()
.setCodecConfig(mCodecConfigSBC)
.setCodecsSelectableCapabilities(mCodecConfigs)
.build();
when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(mCodecStatus);
mController.onBluetoothServiceConnected(mBluetoothA2dp);
mController.writeConfigurationValues(String.valueOf(mCodecTypeSBC.getCodecId()));
verify(mBluetoothA2dpConfigStore, atLeastOnce()).setCodecType(mCodecTypeSBC);
mController.writeConfigurationValues(String.valueOf(mCodecTypeAAC.getCodecId()));
verify(mBluetoothA2dpConfigStore).setCodecType(mCodecTypeAAC);
mController.writeConfigurationValues(String.valueOf(mCodecTypeAPTX.getCodecId()));
verify(mBluetoothA2dpConfigStore).setCodecType(mCodecTypeAPTX);
mController.writeConfigurationValues(String.valueOf(mCodecTypeLDAC.getCodecId()));
verify(mBluetoothA2dpConfigStore).setCodecType(mCodecTypeLDAC);
mController.writeConfigurationValues(String.valueOf(mCodecTypeOPUS.getCodecId()));
verify(mBluetoothA2dpConfigStore).setCodecType(mCodecTypeOPUS);
}
@Test
public void writeConfigurationValues_chooseHighestConfig() {
mCodecStatus =
new BluetoothCodecStatus.Builder()
.setCodecConfig(mCodecConfigSBC)
.setCodecsSelectableCapabilities((mCodecConfigs))
.build();
when(mBluetoothA2dp.getCodecStatus(mActiveDevice)).thenReturn(mCodecStatus);
mController.onBluetoothServiceConnected(mBluetoothA2dp);
mController.writeConfigurationValues(String.valueOf(mCodecTypeAAC.getCodecId()));
verify(mBluetoothA2dpConfigStore, atLeastOnce())
.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST);
verify(mBluetoothA2dpConfigStore, atLeastOnce())
.setSampleRate(BluetoothCodecConfig.SAMPLE_RATE_88200);
verify(mBluetoothA2dpConfigStore, atLeastOnce())
.setBitsPerSample(BluetoothCodecConfig.BITS_PER_SAMPLE_24);
verify(mBluetoothA2dpConfigStore, atLeastOnce())
.setChannelMode(BluetoothCodecConfig.CHANNEL_MODE_STEREO);
}
@Test
public void onPreferenceChange_notifyPreference() {
assertFalse(
mController.onPreferenceChange(
mPreference, String.valueOf(mCodecTypeAAC.getCodecId())));
mController.onBluetoothServiceConnected(mBluetoothA2dp);
assertTrue(
mController.onPreferenceChange(
mPreference, String.valueOf(mCodecTypeAAC.getCodecId())));
verify(mCallback).onBluetoothCodecChanged();
}
@Test
public void onHDAudioEnabled_setsPreferenceEnabled() {
mController.onHDAudioEnabled(/* enabled= */ true);
assertThat(mPreference.isEnabled()).isTrue();
}
}

View File

@@ -58,7 +58,6 @@ import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -203,7 +202,6 @@ public class AppLocalePickerActivityTest {
assertThat(controller.get().isFinishing()).isTrue();
}
@Ignore("b/313604701")
@Test
public void onLocaleSelected_getLocaleNotNull_getLanguageTag() {
ActivityController<TestAppLocalePickerActivity> controller =
@@ -216,7 +214,10 @@ public class AppLocalePickerActivityTest {
AppLocalePickerActivity mActivity = controller.get();
mActivity.onLocaleSelected(mLocaleInfo);
verify(mLocaleInfo, times(2)).getLocale();
// 1st for getLocale()!= null
// 2nd for setAppDefaultLocale(getLocale())
// 3rd for broadcastAppLocaleChange()
verify(mLocaleInfo, times(3)).getLocale();
assertThat(mLocaleInfo.getLocale().toLanguageTag()).isEqualTo("en-US");
assertThat(controller.get().isFinishing()).isTrue();
}

View File

@@ -23,12 +23,9 @@ import android.content.Context
import android.platform.test.flag.junit.SetFlagsRule
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.telephony.flags.Flags
import com.android.settings.network.telephony.CallStateFlowTest
import com.android.settingslib.spa.testutils.toListWithTimeout
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.async
@@ -42,7 +39,6 @@ import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class SubscriptionInfoListViewModelTest {
@@ -62,8 +58,7 @@ class SubscriptionInfoListViewModelTest {
on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
}
private val subscriptionInfoListViewModel: SubscriptionInfoListViewModel =
SubscriptionInfoListViewModel(context as Application);
private fun createViewModel() = SubscriptionInfoListViewModel(context as Application)
private var activeSubscriptionInfoList: List<SubscriptionInfo>? = null
@@ -72,7 +67,7 @@ class SubscriptionInfoListViewModelTest {
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2)
val listDeferred = async {
subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
createViewModel().subscriptionInfoListFlow.toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
@@ -83,49 +78,44 @@ class SubscriptionInfoListViewModelTest {
@Test
fun onSubscriptionsChanged_hasProvisioning_filterProvisioning() = runBlocking {
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3)
val expectation = listOf(SUB_INFO_1, SUB_INFO_2)
val listDeferred = async {
subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
createViewModel().subscriptionInfoListFlow.toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).contains(expectation)
assertThat(listDeferred.await()).contains(listOf(SUB_INFO_1, SUB_INFO_2))
}
@Test
fun onSubscriptionsChanged_flagOffHasNonTerrestrialNetwork_filterNonTerrestrialNetwork() =
runBlocking {
mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
val expectation = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
val listDeferred = async {
subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
createViewModel().subscriptionInfoListFlow.toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).contains(expectation)
assertThat(listDeferred.await()).contains(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4))
}
@Test
fun onSubscriptionsChanged_flagOnHasNonTerrestrialNetwork_filterNonTerrestrialNetwork() =
runBlocking {
mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
val expectation = listOf(SUB_INFO_1, SUB_INFO_2)
val listDeferred = async {
subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
createViewModel().subscriptionInfoListFlow.toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).contains(expectation)
assertThat(listDeferred.await()).contains(listOf(SUB_INFO_1, SUB_INFO_2))
}
private companion object {

View File

@@ -208,6 +208,23 @@ public class PreferredShortcutsTest {
assertThat(savedPreferredShortcut).isEqualTo(UserShortcutType.HARDWARE);
}
@Test
public void retrieveUserShortcutTypeWithoutDefault_noUserPreferredShortcuts_returnSoftwareShortcut() {
String target = COMPONENT_NAME_1.flattenToString();
assertThat(PreferredShortcuts.retrieveUserShortcutType(mContext, target))
.isEqualTo(UserShortcutType.SOFTWARE);
}
@Test
public void retrieveUserShortcutTypeWithDefaultAsDefault_noUserPreferredShortcuts_returnSpecifiedDefault() {
String target = COMPONENT_NAME_1.flattenToString();
assertThat(PreferredShortcuts.retrieveUserShortcutType(mContext, target,
UserShortcutType.HARDWARE))
.isEqualTo(UserShortcutType.HARDWARE);
}
private static void clearShortcuts() {
Settings.Secure.putString(sContentResolver,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "");