Merge "[Audiosharing] Rename CallsAndAlarms components" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9ae77b2..14879e2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -5192,6 +5192,18 @@
android:theme="@style/Theme.SpaLib.Dialog">
</activity>
+ <activity android:name="Settings$BluetoothDashboardActivity"
+ android:label="@string/bluetooth_settings_title"
+ android:permission="android.permission.BLUETOOTH_CONNECT"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.settings.BLUETOOTH_DASHBOARD_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.connecteddevice.BluetoothDashboardFragment"/>
+ </activity>
+
<activity
android:name="com.android.settings.connecteddevice.audiosharing.AudioSharingActivity"
android:label="@string/audio_sharing_title"
diff --git a/res/layout/data_usage_summary_preference.xml b/res/layout/data_usage_summary_preference.xml
index 4cbd958..e678b36 100644
--- a/res/layout/data_usage_summary_preference.xml
+++ b/res/layout/data_usage_summary_preference.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="172dp"
android:paddingTop="8dp"
android:paddingBottom="16dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
@@ -37,7 +38,6 @@
android:id="@+id/usage_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="12dp"
android:orientation="horizontal">
<TextView android:id="@+id/data_usage_view"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e62bf14..fa90d07 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1240,6 +1240,28 @@
<string name="private_space_fingerprint_unlock_title">Fingerprint Unlock for private space</string>
<!-- Title for the Face unlock for private space preference. [CHAR LIMIT=60] -->
<string name="private_space_face_unlock_title">Face Unlock for private space</string>
+ <!-- Title for the Face and Fingerprint preference for private space. [CHAR LIMIT=60] -->
+ <string name="private_space_biometric_unlock_title">Face & Fingerprint Unlock for private space</string>
+ <!-- Introduction title shown in private space fingerprint enrollment [CHAR LIMIT=90] -->
+ <string name="private_space_fingerprint_enroll_introduction_title">Set up Fingerprint Unlock for private space</string>
+ <!-- Introduction detail message shown in private space fingerprint enrollment dialog [CHAR LIMIT=NONE]-->
+ <string name ="private_space_fingerprint_enroll_introduction_message">Use your fingerprint to unlock your private space or verify it\u2019s you, like when you sign in to apps or approve a purchase</string>
+ <!-- Introduction description message shown in private space fingerprint enrollment introduction screen. [CHAR LIMIT=NONE] -->
+ <string name="private_space_fingerprint_enroll_introduction_footer_message">Your private space can be unlocked when you don\u2019t intend to, like if someone holds up your phone to your finger.</string>
+ <!-- Message shown in fingerprint enrollment dialog once enrollment is completed (private space) [CHAR LIMIT=NONE] -->
+ <string name="private_space_fingerprint_enroll_finish_message">Use your fingerprint to unlock your private space or to approve purchases</string>
+ <!-- Introduction title shown in private space face enrollment [CHAR LIMIT=90] -->
+ <string name="private_space_face_enroll_introduction_title">Set up Face Unlock for private space</string>
+ <!-- Introduction detail message shown in private space face enrollment dialog [CHAR LIMIT=NONE]-->
+ <string name ="private_space_face_enroll_introduction_message">Use your face to unlock your private space or verify it\u2019s you, like when you sign in to apps or approve a purchase</string>
+ <!-- Message on the face enrollment introduction page for private space that provides information about what could cause the phone to unlock. [CHAR LIMIT=NONE] -->
+ <string name="private_space_face_enroll_introduction_info_looking">Looking at the phone can unlock private space even when you don\u2019t intend to. Your private space can also be unlocked by someone who looks a lot like you, like an identical sibling, or if someone holds the device up to your face.</string>
+ <!-- Message on the face enrollment introduction page for private space that provides information about the relative security of face for unlocking the phone. [CHAR LIMIT=NONE] -->
+ <string name="private_space_face_enroll_introduction_info_less_secure">Using your face to unlock your private space may be less secure than a strong pattern, PIN, or password</string>
+ <!-- Text shown on the details of a toggle which disables/enables face unlock for private space, depending if the user's eyes are open. [CHAR LIMIT=NONE] -->
+ <string name="private_space_face_settings_require_attention_details">To unlock private space, your eyes must be open. For best results, take off sunglasses.</string>
+ <!-- Text shown in face settings in private space explaining what your face can be used for. [CHAR LIMIT=NONE] -->
+ <string name="private_space_face_settings_footer">Use your face to unlock your private space.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour private space can be unlocked by someone else if your device is held up to your face.\n\nYour private space can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
<!-- Biometric category title - biometric options for unlocking the device. [CHAR LIMIT=50] -->
<string name="private_space_category_ways_to_unlock">Ways to unlock</string>
<!-- Summary for one lock when device screen lock is used as private profile lock. [CHAR LIMIT=40] -->
@@ -3408,6 +3430,8 @@
<string name="error_name_empty">The Name field can\u2019t be empty.</string>
<!-- APN error dialog messages: -->
<string name="error_apn_empty">The APN can\u2019t be empty.</string>
+ <!-- APN error message about APN type cannot be empty -->
+ <string name="error_apn_type_empty">The APN type can\u2019t be empty.</string>
<!-- APN error dialog messages: -->
<string name="error_mcc_not3">MCC field must be 3 digits.</string>
<!-- APN error dialog messages: -->
@@ -11406,7 +11430,7 @@
<string name="media_output_default_summary">This device</string>
<!-- Summary for media output when audio sharing. [CHAR LIMIT=NONE] -->
- <string name="media_output_audio_sharing">Audio sharing</string>
+ <string name="media_output_audio_sharing">Sharing audio</string>
<!-- Summary for media output settings when device is in ongoing call state. -->
<string name="media_out_summary_ongoing_call_state">Unavailable during calls</string>
diff --git a/res/xml/bluetooth_le_audio_sharing.xml b/res/xml/bluetooth_le_audio_sharing.xml
index 85a6468..15c38b3 100644
--- a/res/xml/bluetooth_le_audio_sharing.xml
+++ b/res/xml/bluetooth_le_audio_sharing.xml
@@ -68,14 +68,14 @@
<PreferenceCategory
android:key="audio_streams_settings_category"
- android:title="@string/audio_streams_category_title"
+ android:title="@string/audio_sharing_nearby_audio_title"
settings:controller="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController">
<Preference
android:fragment="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsDashboardFragment"
android:icon="@drawable/ic_chevron_right_24dp"
android:key="audio_streams_settings"
- android:title="@string/audio_streams_pref_title" />
+ android:title="@string/audio_streams_main_page_title" />
</PreferenceCategory>
</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/private_space_biometric_settings.xml b/res/xml/private_space_biometric_settings.xml
index 6135b5f..38ec09a 100644
--- a/res/xml/private_space_biometric_settings.xml
+++ b/res/xml/private_space_biometric_settings.xml
@@ -17,7 +17,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
- android:title="@string/private_space_biometric_title"
+ android:title="@string/private_space_biometric_unlock_title"
settings:searchable="false">
<com.android.settingslib.widget.TopIntroPreference
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 9ebc124..3367bf1 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -47,6 +47,7 @@
public static class MemtagPageActivity extends SettingsActivity { /* empty */}
public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
+ public static class BluetoothDashboardActivity extends SettingsActivity { /* empty */ }
public static class CreateShortcutActivity extends SettingsActivity { /* empty */ }
public static class FaceSettingsActivity extends SettingsActivity { /* empty */ }
/** Container for {@link FaceSettings} to use with a pre-defined task affinity. */
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index f76ea27..145f3a4 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1401,6 +1401,16 @@
}
/**
+ * Returns true if the userId is a private profile, false otherwise.
+ */
+ public static boolean isPrivateProfile(int userId, @NonNull Context context) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ UserInfo userInfo = userManager.getUserInfo(userId);
+ return Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && userInfo.isPrivateProfile();
+ }
+
+ /**
* Enable new edge to edge feature.
*
* @param activity the Activity need to setup the edge to edge feature.
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
index 2cd239e..92b6d60 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
@@ -93,7 +93,10 @@
public String getTitle() {
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager != null && userManager.isProfile()) {
- return mContext.getString(R.string.security_settings_work_biometric_preference_title);
+ return mContext.getString(
+ Utils.isPrivateProfile(mUserId, mContext)
+ ? R.string.private_space_biometric_unlock_title
+ : R.string.security_settings_work_biometric_preference_title);
} else {
return mContext.getString(R.string.security_settings_biometric_preference_title);
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index 414c545..4888388 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -365,7 +365,9 @@
@StringRes
protected int getInfoMessageLooking() {
- return R.string.security_settings_face_enroll_introduction_info_looking;
+ return isPrivateProfile()
+ ? R.string.private_space_face_enroll_introduction_info_looking
+ : R.string.security_settings_face_enroll_introduction_info_looking;
}
@StringRes
@@ -390,7 +392,10 @@
@StringRes
protected int getLessSecureMessage() {
- return R.string.security_settings_face_enroll_introduction_info_less_secure;
+ return isPrivateProfile()
+ ? R.string.private_space_face_enroll_introduction_info_less_secure
+ : R.string.security_settings_face_enroll_introduction_info_less_secure;
+
}
@Override
@@ -411,6 +416,9 @@
@Override
protected int getHeaderResDefault() {
+ if (isPrivateProfile()) {
+ return R.string.private_space_face_enroll_introduction_title;
+ }
return R.string.security_settings_face_enroll_introduction_title;
}
@@ -577,7 +585,10 @@
@Override
protected void updateDescriptionText() {
- if (mIsFaceStrong) {
+ if (isPrivateProfile()) {
+ setDescriptionText(getString(
+ R.string.private_space_face_enroll_introduction_message));
+ } else if (mIsFaceStrong) {
setDescriptionText(getString(
R.string.security_settings_face_enroll_introduction_message_class3));
}
@@ -608,4 +619,8 @@
}
updateDescriptionText();
}
+
+ private boolean isPrivateProfile() {
+ return Utils.isPrivateProfile(mUserId, getApplicationContext());
+ }
}
diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java
index 24df51f..061487a 100644
--- a/src/com/android/settings/biometrics/face/FaceSettings.java
+++ b/src/com/android/settings/biometrics/face/FaceSettings.java
@@ -211,6 +211,8 @@
((FaceSettingsPreferenceController) controller).setUserId(mUserId);
} else if (controller instanceof FaceSettingsEnrollButtonPreferenceController) {
((FaceSettingsEnrollButtonPreferenceController) controller).setUserId(mUserId);
+ } else if (controller instanceof FaceSettingsFooterPreferenceController) {
+ ((FaceSettingsFooterPreferenceController) controller).setUserId(mUserId);
}
}
mRemoveController.setUserId(mUserId);
@@ -367,6 +369,7 @@
controllers.add(new FaceSettingsRemoveButtonPreferenceController(context));
controllers.add(new FaceSettingsConfirmPreferenceController(context));
controllers.add(new FaceSettingsEnrollButtonPreferenceController(context));
+ controllers.add(new FaceSettingsFooterPreferenceController(context));
return controllers;
}
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
index 77a160d..71c4678 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
@@ -24,10 +24,12 @@
import android.hardware.face.FaceManager.SetFeatureCallback;
import android.provider.Settings;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
+import com.android.settings.R;
import com.android.settings.Utils;
/**
@@ -99,6 +101,18 @@
}
@Override
+ public void updateState(@Nullable Preference preference) {
+ if (preference == null) {
+ return;
+ }
+ super.updateState(preference);
+ if (Utils.isPrivateProfile(getUserId(), mContext)) {
+ preference.setSummary(mContext.getString(
+ R.string.private_space_face_settings_require_attention_details));
+ }
+ }
+
+ @Override
public boolean isChecked() {
if (!FaceSettings.isFaceHardwareDetected(mContext)) {
return true;
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java
index e2123f0..d56c9b3 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java
@@ -30,6 +30,7 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AnnotationSpan;
@@ -41,12 +42,17 @@
* Footer for face settings showing the help text and help link.
*/
public class FaceSettingsFooterPreferenceController extends BasePreferenceController {
+ private static final String KEY = "security_face_footer";
private static final String TAG = "FaceSettingsFooterPreferenceController";
private static final String ANNOTATION_URL = "url";
private final FaceFeatureProvider mProvider;
private Preference mPreference;
private boolean mIsFaceStrong;
+ private int mUserId;
+ public FaceSettingsFooterPreferenceController(@NonNull Context context) {
+ this(context, KEY);
+ }
public FaceSettingsFooterPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mProvider = FeatureFactory.getFeatureFactory().getFaceFeatureProvider();
@@ -79,7 +85,9 @@
int footerRes;
boolean isAttentionSupported = mProvider.isAttentionSupported(mContext);
- if (mIsFaceStrong) {
+ if (Utils.isPrivateProfile(mUserId, mContext)) {
+ footerRes = R.string.private_space_face_settings_footer;
+ } else if (mIsFaceStrong) {
footerRes = isAttentionSupported
? R.string.security_settings_face_settings_footer_class3
: R.string.security_settings_face_settings_footer_attention_not_supported;
@@ -92,6 +100,10 @@
mContext.getText(footerRes), linkInfo));
}
+ public void setUserId(int userId) {
+ mUserId = userId;
+ }
+
private void addAuthenticatorsRegisteredCallback(Context context) {
final FaceManager faceManager = context.getSystemService(FaceManager.class);
faceManager.addAuthenticatorsRegisteredCallback(
diff --git a/src/com/android/settings/biometrics/face/FaceStatusUtils.java b/src/com/android/settings/biometrics/face/FaceStatusUtils.java
index d02e115..5af0a8a 100644
--- a/src/com/android/settings/biometrics/face/FaceStatusUtils.java
+++ b/src/com/android/settings/biometrics/face/FaceStatusUtils.java
@@ -67,7 +67,10 @@
public String getTitle() {
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager != null && userManager.isProfile()) {
- return mContext.getString(R.string.security_settings_face_profile_preference_title);
+ return mContext.getString(
+ Utils.isPrivateProfile(mUserId, mContext)
+ ? R.string.private_space_face_unlock_title
+ : R.string.security_settings_face_profile_preference_title);
} else {
return mContext.getString(R.string.security_settings_face_preference_title);
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
index 9c89f24..013e3d7 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
@@ -78,7 +78,10 @@
setContentView(R.layout.fingerprint_enroll_finish);
}
setHeaderText(R.string.security_settings_fingerprint_enroll_finish_title);
- setDescriptionText(R.string.security_settings_fingerprint_enroll_finish_v2_message);
+ setDescriptionText(Utils.isPrivateProfile(mUserId, getApplicationContext())
+ ? R.string.private_space_fingerprint_enroll_finish_message
+ : R.string.security_settings_fingerprint_enroll_finish_v2_message);
+
final String sfpsDescription = mSfpsRestToUnlockFeature != null
? mSfpsRestToUnlockFeature.getDescriptionForSfps(this)
: null;
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index a1f4eff..f6626b2 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -176,7 +176,9 @@
@Override
protected void initViews() {
setDescriptionText(getString(
- R.string.security_settings_fingerprint_enroll_introduction_v3_message,
+ isPrivateProfile()
+ ? R.string.private_space_fingerprint_enroll_introduction_message
+ : R.string.security_settings_fingerprint_enroll_introduction_v3_message,
DeviceHelper.getDeviceName(this)));
super.initViews();
@@ -261,6 +263,9 @@
@StringRes
protected int getFooterMessage5() {
+ if (isPrivateProfile()) {
+ return R.string.private_space_fingerprint_enroll_introduction_footer_message;
+ }
return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5;
}
@@ -292,6 +297,9 @@
@Override
protected int getHeaderResDefault() {
+ if (isPrivateProfile()) {
+ return R.string.private_space_fingerprint_enroll_introduction_title;
+ }
return R.string.security_settings_fingerprint_enroll_introduction_title;
}
@@ -477,4 +485,8 @@
data.putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, true);
return data;
}
+
+ private boolean isPrivateProfile() {
+ return Utils.isPrivateProfile(mUserId, getApplicationContext());
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 63499a5..7651176 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -510,8 +510,9 @@
mFooterColumns.add(column2);
} else {
final FooterColumn column = new FooterColumn();
- column.mTitle = getString(
- R.string.security_settings_fingerprint_enroll_introduction_v3_message,
+ column.mTitle = getString(isPrivateProfile()
+ ? R.string.private_space_fingerprint_enroll_introduction_message
+ : R.string.security_settings_fingerprint_enroll_introduction_v3_message,
DeviceHelper.getDeviceName(getActivity()));
column.mLearnMoreClickListener = learnMoreClickListener;
column.mLearnMoreOverrideText = getText(
@@ -1130,6 +1131,10 @@
}
};
+ private boolean isPrivateProfile() {
+ return Utils.isPrivateProfile(mUserId, getContext());
+ }
+
public static class DeleteFingerprintDialog extends InstrumentedDialogFragment
implements DialogInterface.OnClickListener {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
index d6e6498..a7c9e9d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
@@ -68,7 +68,10 @@
public String getTitle() {
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager != null && userManager.isProfile()) {
- return mContext.getString(R.string.security_settings_work_fingerprint_preference_title);
+ return mContext.getString(
+ Utils.isPrivateProfile(mUserId, mContext)
+ ? R.string.private_space_fingerprint_unlock_title
+ : R.string.security_settings_work_fingerprint_preference_title);
} else {
return mContext.getString(R.string.security_settings_fingerprint_preference_title);
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index e017143..44915fe 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -66,6 +66,7 @@
public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment {
public static final String KEY_DEVICE_ADDRESS = "device_address";
private static final String TAG = "BTDeviceDetailsFrg";
+ private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
@VisibleForTesting
static int EDIT_DEVICE_NAME_ITEM_ID = Menu.FIRST;
@@ -95,11 +96,14 @@
LocalBluetoothManager mManager;
@VisibleForTesting
CachedBluetoothDevice mCachedDevice;
+ BluetoothAdapter mBluetoothAdapter;
@Nullable
InputDevice mInputDevice;
private UserManager mUserManager;
+ int mExtraControlViewWidth = 0;
+ boolean mExtraControlUriLoaded = false;
private final BluetoothCallback mBluetoothCallback =
new BluetoothCallback() {
@@ -115,6 +119,16 @@
}
};
+ private final BluetoothAdapter.OnMetadataChangedListener mExtraControlMetadataListener =
+ (device, key, value) -> {
+ if (key == METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
+ && mExtraControlViewWidth > 0
+ && !mExtraControlUriLoaded) {
+ Log.i(TAG, "Update extra control UI because of metadata change.");
+ updateExtraControlUri(mExtraControlViewWidth);
+ }
+ };
+
public BluetoothDeviceDetailsFragment() {
super(DISALLOW_CONFIG_BLUETOOTH);
}
@@ -173,6 +187,7 @@
public void onAttach(Context context) {
mDeviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS);
mManager = getLocalBluetoothManager(context);
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mCachedDevice = getCachedDevice(mDeviceAddress);
mUserManager = getUserManager();
@@ -202,12 +217,18 @@
: null);
mManager.getEventManager().registerCallback(mBluetoothCallback);
+ mBluetoothAdapter.addOnMetadataChangedListener(
+ mCachedDevice.getDevice(),
+ context.getMainExecutor(),
+ mExtraControlMetadataListener);
}
@Override
public void onDetach() {
super.onDetach();
mManager.getEventManager().unregisterCallback(mBluetoothCallback);
+ mBluetoothAdapter.removeOnMetadataChangedListener(
+ mCachedDevice.getDevice(), mExtraControlMetadataListener);
}
private void updateExtraControlUri(int viewWidth) {
@@ -222,9 +243,9 @@
controlUri = Uri.parse(uri + viewWidth);
} catch (NullPointerException exception) {
Log.d(TAG, "unable to parse uri");
- controlUri = null;
}
}
+ mExtraControlUriLoaded |= controlUri != null;
final SlicePreferenceController slicePreferenceController = use(
SlicePreferenceController.class);
slicePreferenceController.setSliceUri(sliceEnabled ? controlUri : null);
@@ -253,7 +274,8 @@
if (view.getWidth() <= 0) {
return;
}
- updateExtraControlUri(view.getWidth() - getPaddingSize());
+ mExtraControlViewWidth = view.getWidth() - getPaddingSize();
+ updateExtraControlUri(mExtraControlViewWidth);
view.getViewTreeObserver().removeOnGlobalLayoutListener(
mOnGlobalLayoutListener);
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
index 2e4930c..ea5eede 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
@@ -42,6 +42,7 @@
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
@@ -60,6 +61,7 @@
private static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action";
private static final String LEAVE_BROADCAST_TEXT = "Leave Broadcast";
private static final String CHANNEL_ID = "bluetooth_notification_channel";
+ private static final String DEFAULT_DEVICE_NAME = "";
private static final int STATIC_PLAYBACK_DURATION = 100;
private static final int STATIC_PLAYBACK_POSITION = 30;
private static final int ZERO_PLAYBACK_SPEED = 0;
@@ -355,16 +357,34 @@
return mIsMuted ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
}
+ private String getDeviceName() {
+ if (mDevices == null || mDevices.isEmpty() || mLocalBtManager == null) {
+ return DEFAULT_DEVICE_NAME;
+ }
+
+ CachedBluetoothDeviceManager manager = mLocalBtManager.getCachedDeviceManager();
+ if (manager == null) {
+ return DEFAULT_DEVICE_NAME;
+ }
+
+ CachedBluetoothDevice device = manager.findDevice(mDevices.get(0));
+ return device != null ? device.getName() : DEFAULT_DEVICE_NAME;
+ }
+
private Notification buildNotification() {
+ String deviceName = getDeviceName();
+ Notification.MediaStyle mediaStyle =
+ new Notification.MediaStyle()
+ .setMediaSession(
+ mLocalSession != null ? mLocalSession.getSessionToken() : null);
+ if (deviceName != null && !deviceName.isEmpty()) {
+ mediaStyle.setRemotePlaybackInfo(
+ deviceName, com.android.internal.R.drawable.ic_bt_headset_hfp, null);
+ }
Notification.Builder notificationBuilder =
new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
- .setStyle(
- new Notification.MediaStyle()
- .setMediaSession(
- mLocalSession != null
- ? mLocalSession.getSessionToken()
- : null))
+ .setStyle(mediaStyle)
.setContentText(this.getString(BROADCAST_CONTENT_TEXT))
.setSilent(true);
return notificationBuilder.build();
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 7c601c0..1c14712 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -86,6 +86,7 @@
import com.android.settings.bugreporthandler.BugReportHandlerPicker;
import com.android.settings.communal.CommunalDashboardFragment;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
+import com.android.settings.connecteddevice.BluetoothDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.NfcAndPaymentFragment;
import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment;
@@ -213,6 +214,7 @@
AdvancedConnectedDeviceDashboardFragment.class.getName(),
CreateShortcut.class.getName(),
BluetoothPairingDetail.class.getName(),
+ BluetoothDashboardFragment.class.getName(),
WifiNetworkDetailsFragment.class.getName(),
ConfigureWifiSettings.class.getName(),
SavedAccessPointsWifiSettings2.class.getName(),
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreference.java b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
index 93d930c..6500501 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreference.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.graphics.Typeface;
import android.icu.text.MessageFormat;
+import android.telephony.SubscriptionPlan;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -66,7 +67,7 @@
@Nullable
private Long mCycleEndTimeMs;
/** The time of the last update in standard milliseconds since the epoch */
- private long mSnapshotTimeMs;
+ private long mSnapshotTimeMs = SubscriptionPlan.TIME_UNKNOWN;
/** Name of carrier, or null if not available */
private CharSequence mCarrierName;
private CharSequence mLimitInfoText;
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
index 37d2fce..4acaeea 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
@@ -767,6 +767,10 @@
}
private class BatteryChartAccessibilityNodeProvider extends AccessibilityNodeProvider {
+ private static final int UNDEFINED = Integer.MIN_VALUE;
+
+ private int mAccessibilityFocusNodeViewId = UNDEFINED;
+
@Override
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) {
@@ -794,6 +798,7 @@
R.string.battery_usage_time_info_and_battery_level,
slotTimeInfo,
batteryLevelInfo));
+ childInfo.setAccessibilityFocused(virtualViewId == mAccessibilityFocusNodeViewId);
final Rect bounds = new Rect();
getBoundsOnScreen(bounds, true);
@@ -815,10 +820,14 @@
return true;
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+ mAccessibilityFocusNodeViewId = virtualViewId;
return sendAccessibilityEvent(
virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+ if (mAccessibilityFocusNodeViewId == virtualViewId) {
+ mAccessibilityFocusNodeViewId = UNDEFINED;
+ }
return sendAccessibilityEvent(
virtualViewId,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index aa816fc..2f41f23 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -174,20 +174,13 @@
* @return An error message if the apn data is invalid, otherwise return null.
*/
fun validateApnData(apnData: ApnData, context: Context): String? {
- var errorMsg: String?
- val name = apnData.name
- val apn = apnData.apn
- errorMsg = if (name == "") {
- context.resources.getString(R.string.error_name_empty)
- } else if (apn == "") {
- context.resources.getString(R.string.error_apn_empty)
- } else {
- validateMMSC(true, apnData.mmsc, context)
+ val errorMsg: String? = when {
+ apnData.name.isEmpty() -> context.resources.getString(R.string.error_name_empty)
+ apnData.apn.isEmpty() -> context.resources.getString(R.string.error_apn_empty)
+ apnData.apnType.isEmpty() -> context.resources.getString(R.string.error_apn_type_empty)
+ else -> validateMMSC(true, apnData.mmsc, context) ?: isItemExist(apnData, context)
}
- if (errorMsg == null) {
- errorMsg = isItemExist(apnData, context)
- }
- return errorMsg?.apply { Log.d(TAG, "APN data not valid, reason: $this") }
+ return errorMsg?.also { Log.d(TAG, "APN data not valid, reason: $it") }
}
/**
diff --git a/src/com/android/settings/network/apn/ApnTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
index aa757cc..f4e73fb 100644
--- a/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
+++ b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt
@@ -24,6 +24,7 @@
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
+import com.android.settings.network.apn.ApnTypes.isValid
import com.android.settings.network.apn.ApnTypes.toApnType
import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
@@ -47,6 +48,8 @@
label = stringResource(R.string.apn_type),
options = apnTypeOptions,
enabled = apnData.isFieldEnabled(Telephony.Carriers.TYPE),
+ errorMessage = stringResource(R.string.error_apn_type_empty)
+ .takeUnless { apnTypeOptions.isValid() },
) {
onTypeChanged(apnTypeOptions.toApnType())
updateMmsSelected()
diff --git a/src/com/android/settings/network/apn/ApnTypes.kt b/src/com/android/settings/network/apn/ApnTypes.kt
index d384625..b9bc480 100644
--- a/src/com/android/settings/network/apn/ApnTypes.kt
+++ b/src/com/android/settings/network/apn/ApnTypes.kt
@@ -101,6 +101,8 @@
}
}
+ fun List<SettingsDropdownCheckOption>.isValid(): Boolean = any { it.selected.value }
+
fun List<SettingsDropdownCheckOption>.toApnType(): String {
val (selectAllOptions, regularOptions) = partition { it.isSelectAll }
for (selectAllOption in selectAllOptions) {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
index 545f773..704637f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
@@ -43,6 +43,7 @@
import java.time.Clock;
import java.util.List;
+import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
/** Tests of {@link BootBroadcastReceiver}. */
@@ -56,6 +57,7 @@
@Before
public void setUp() {
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
mContext = ApplicationProvider.getApplicationContext();
mPeriodicJobManager = PeriodicJobManager.getInstance(mContext);
mShadowAlarmManager = shadowOf(mContext.getSystemService(AlarmManager.class));
@@ -136,7 +138,7 @@
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
- TimeUnit.MILLISECONDS.sleep(100);
+ TimeUnit.MILLISECONDS.sleep(1000);
assertThat(mDao.getAllAfter(0)).isEmpty();
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
}
@@ -150,7 +152,7 @@
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
- TimeUnit.MILLISECONDS.sleep(100);
+ TimeUnit.MILLISECONDS.sleep(1000);
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
}
@@ -168,7 +170,7 @@
Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,
Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR));
- TimeUnit.MILLISECONDS.sleep(100);
+ TimeUnit.MILLISECONDS.sleep(1000);
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
}
@@ -182,7 +184,7 @@
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIMEZONE_CHANGED));
- TimeUnit.MILLISECONDS.sleep(100);
+ TimeUnit.MILLISECONDS.sleep(1000);
assertThat(mDao.getAllAfter(0)).isEmpty();
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
}
diff --git a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUtils.java
index 5f8c434..ed03bcc 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUtils.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUtils.java
@@ -50,6 +50,7 @@
private static ArraySet<String> sResultLinks = new ArraySet<>();
private static boolean sIsBatteryPresent;
private static boolean sIsMultipleBiometricsSupported;
+ private static boolean sIsPrivateProfile;
@Implementation
protected static int enforceSameOwner(Context context, int userId) {
@@ -82,6 +83,7 @@
sResultLinks = new ArraySet<>();
sIsBatteryPresent = true;
sIsMultipleBiometricsSupported = false;
+ sIsPrivateProfile = false;
}
public static void setIsDemoUser(boolean isDemoUser) {
@@ -188,4 +190,13 @@
public static void setIsMultipleBiometricsSupported(boolean isMultipleBiometricsSupported) {
sIsMultipleBiometricsSupported = isMultipleBiometricsSupported;
}
+
+ @Implementation
+ protected static boolean isPrivateProfile(int userId, Context context) {
+ return sIsPrivateProfile;
+ }
+
+ public static void setIsPrivateProfile(boolean isPrivateProfile) {
+ sIsPrivateProfile = isPrivateProfile;
+ }
}
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt
index 95471b0..ce0d0f5 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt
@@ -17,7 +17,11 @@
package com.android.settings.network.apn
import android.telephony.data.ApnSetting
+import androidx.compose.runtime.mutableStateOf
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.network.apn.ApnTypes.isValid
+import com.android.settings.network.apn.ApnTypes.toApnType
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -26,6 +30,50 @@
class ApnTypesTest {
@Test
+ fun isValid_hasSelected() {
+ val options = listOf(
+ SettingsDropdownCheckOption(text = APN_TYPE, selected = mutableStateOf(true)),
+ )
+
+ val isValid = options.isValid()
+
+ assertThat(isValid).isTrue()
+ }
+
+ @Test
+ fun isValid_hasNotSelected() {
+ val options = listOf(
+ SettingsDropdownCheckOption(text = APN_TYPE, selected = mutableStateOf(false)),
+ )
+
+ val isValid = options.isValid()
+
+ assertThat(isValid).isFalse()
+ }
+
+ @Test
+ fun toApnType_hasSelected() {
+ val options = listOf(
+ SettingsDropdownCheckOption(text = APN_TYPE, selected = mutableStateOf(true)),
+ )
+
+ val apnType = options.toApnType()
+
+ assertThat(apnType).isEqualTo(APN_TYPE)
+ }
+
+ @Test
+ fun toApnType_hasNotSelected() {
+ val options = listOf(
+ SettingsDropdownCheckOption(text = APN_TYPE, selected = mutableStateOf(false)),
+ )
+
+ val apnType = options.toApnType()
+
+ assertThat(apnType).isEmpty()
+ }
+
+ @Test
fun getPreSelectedApnType_regular() {
val apnType = ApnTypes.getPreSelectedApnType(CustomizedConfig())
@@ -42,4 +90,8 @@
assertThat(apnType).isEqualTo("default,mms,supl,hipri,fota,cbs,xcap")
}
+
+ private companion object {
+ const val APN_TYPE = "type"
+ }
}