Merge "Add audio category preference in more settings fragment" into main
diff --git a/Android.bp b/Android.bp
index 0a58ee8..28c3148 100644
--- a/Android.bp
+++ b/Android.bp
@@ -130,6 +130,7 @@
"ims-common",
],
flags_packages: [
+ "aconfig_settings_flags",
"android.app.flags-aconfig",
],
}
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cc4d898..5072e67 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -237,6 +237,7 @@
<intent-filter>
<action android:name="android.intent.action.USER_INITIALIZE"/>
<action android:name="android.intent.action.PRE_BOOT_COMPLETED"/>
+ <action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED"/>
</intent-filter>
</receiver>
diff --git a/res-product/values-pt-rPT/strings.xml b/res-product/values-pt-rPT/strings.xml
index 25f8b74..67c7b74 100644
--- a/res-product/values-pt-rPT/strings.xml
+++ b/res-product/values-pt-rPT/strings.xml
@@ -266,7 +266,7 @@
<string name="ethernet_tethering_subtext" product="default" msgid="8652438909365718644">"Partilhar a ligação à Internet do telemóvel através de Ethernet"</string>
<string name="ethernet_tethering_subtext" product="tablet" msgid="2227710549796706455">"Partilhar a ligação à Internet do tablet por Ethernet"</string>
<string name="about_settings" product="tablet" msgid="1471390492111370330">"Acerca do tablet"</string>
- <string name="about_settings" product="default" msgid="2621311564780208250">"Acerca do telefone"</string>
+ <string name="about_settings" product="default" msgid="2621311564780208250">"Acerca do telemóvel"</string>
<string name="about_settings" product="device" msgid="7595574154492383452">"Acerca do dispositivo"</string>
<string name="about_settings" product="emulator" msgid="1099246296173401003">"Acerca do dispositivo emulado"</string>
<string name="install_all_warning" product="tablet" msgid="1732116924846572063">"O seu tablet e os dados pessoais são mais vulneráveis a ataques de apps desconhecidas. Ao instalar apps desta fonte, aceita ser responsável por quaisquer danos no tablet ou perdas de dados que possam resultar da utilização do mesmo."</string>
diff --git a/res-product/values-zh-rCN/strings.xml b/res-product/values-zh-rCN/strings.xml
index c11fa41..873d35e 100644
--- a/res-product/values-zh-rCN/strings.xml
+++ b/res-product/values-zh-rCN/strings.xml
@@ -297,8 +297,8 @@
<string name="battery_tip_dialog_summary_message" product="device" msgid="7885502661524685786">"您的应用目前耗电量正常。如果应用耗电量过高,您的设备会为您提供操作建议。\n\n如果电池电量不足,您可以随时开启省电模式。"</string>
<string name="smart_battery_summary" product="default" msgid="1210637215867635435">"限制不常用的应用的耗电量"</string>
<string name="battery_usage_screen_footer" product="default" msgid="8872101342490341865">"手机充电时,系统不会衡量电池使用情况和屏幕使用时间"</string>
- <string name="battery_usage_screen_footer" product="tablet" msgid="1876984641036532124">"平板电脑充电时,系统不会衡量电池用量和设备使用时间"</string>
- <string name="battery_usage_screen_footer" product="device" msgid="6488857833906266507">"设备充电时,系统不会衡量电池用量和设备使用时间"</string>
+ <string name="battery_usage_screen_footer" product="tablet" msgid="1876984641036532124">"平板电脑充电时,系统不会衡量电池用量和屏幕使用时间"</string>
+ <string name="battery_usage_screen_footer" product="device" msgid="6488857833906266507">"设备充电时,系统不会衡量电池用量和屏幕使用时间"</string>
<string name="credentials_install_summary" product="nosdcard" msgid="8585932964626513863">"从存储设备安装证书"</string>
<string name="credentials_install_summary" product="default" msgid="879796378361350092">"从SD卡安装证书"</string>
<string name="really_remove_account_message" product="tablet" msgid="5134483498496943623">"移除该账号会从平板电脑中删除所有相关的邮件、联系人以及其他数据。"</string>
diff --git a/res/layout/modes_edit_name.xml b/res/layout/modes_edit_name.xml
index 0b086c7..7f1a1e6 100644
--- a/res/layout/modes_edit_name.xml
+++ b/res/layout/modes_edit_name.xml
@@ -20,7 +20,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="8dp">
<EditText
android:layout_width="match_parent"
diff --git a/res/layout/modes_icon_list.xml b/res/layout/modes_icon_list.xml
index f6f2202..b189ce6 100644
--- a/res/layout/modes_icon_list.xml
+++ b/res/layout/modes_icon_list.xml
@@ -28,8 +28,7 @@
android:layout_height="wrap_content"
android:clipToPadding="true"
android:nestedScrollingEnabled="false"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
+ android:padding="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
diff --git a/res/layout/modes_set_schedule_layout.xml b/res/layout/modes_set_schedule_layout.xml
index e90dc7c..6b7ebc8 100644
--- a/res/layout/modes_set_schedule_layout.xml
+++ b/res/layout/modes_set_schedule_layout.xml
@@ -127,7 +127,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Small" />
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Small"
+ android:textColor="?android:attr/textColorSecondary" />
<!-- right side line divider -->
<View
diff --git a/res/layout/sfps_enroll_finish_base.xml b/res/layout/sfps_enroll_finish_base.xml
index 9e65c83..768fe34 100644
--- a/res/layout/sfps_enroll_finish_base.xml
+++ b/res/layout/sfps_enroll_finish_base.xml
@@ -35,7 +35,7 @@
android:id="@+id/sfps_enrollment_finish_content_layout"
android:layout_width="@dimen/sfps_enrollment_finished_icon_max_size"
android:layout_height="@dimen/sfps_enrollment_finished_icon_max_size"
- android:layout_marginTop="24dp"
+ android:layout_marginTop="@dimen/sfps_enroll_finish_icon_margin_top"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:layout_gravity="center">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c9a67e4..5961b95 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -156,6 +156,7 @@
<dimen name="sfps_progress_bar_translate_y">2dp</dimen>
<dimen name="sfps_lottie_translate_x">12dp</dimen>
<dimen name="sfps_lottie_translate_y">12dp</dimen>
+ <dimen name="sfps_enroll_finish_icon_margin_top">-24dp</dimen>
<dimen name="udfps_lottie_translate_y">0dp</dimen>
<dimen name="udfps_lottie_padding_top">20dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4780e52..cf0e112 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -571,11 +571,15 @@
<string name="roaming_warning">Roaming charges may apply.</string>
<!-- Date & time setting screen setting switch title: whether the system clock (Unix epoch time) should be determined automatically [CHAR LIMIT=100] -->
- <string name="date_time_auto">Set time automatically</string>
+ <string name="date_time_auto">Automatic date and time</string>
+ <!-- Date & time setting screen setting switch summary: whether the system clock (Unix epoch time) should be determined automatically [CHAR LIMIT=100] -->
+ <string name="date_time_auto_summary">Set automatically using your network and wireless signals</string>
<!-- Date & time setting screen setting switch title: whether the time zone should be determined automatically [CHAR LIMIT=100] -->
- <string name="zone_auto_title">Set automatically</string>
+ <string name="zone_auto_title">Automatic time zone</string>
+ <!-- Date & time setting screen setting switch summary: whether the time zone should be determined automatically [CHAR LIMIT=100] -->
+ <string name="zone_auto_title_summary">Set automatically based on mobile networks near you</string>
<!-- Date & time setting screen setting switch summary for non-telephony devices [CHAR LIMIT=100] -->
- <string name="auto_zone_requires_location_summary">Location will be used for setting the time zone when this toggle is on</string>
+ <string name="auto_zone_requires_location_summary">Set automatically using your device location, if available. An active Wifi connection may also be required.</string>
<!-- Date & time setting screen setting option summary text for the automatic 24 hour setting checkbox [CHAR LIMIT=100] -->
<string name="date_time_24hour_auto">Use locale default</string>
<!-- Date & time setting screen setting check box title -->
@@ -3772,7 +3776,7 @@
<string name="location_services_screen_title">Location services</string>
<!-- [CHAR LIMIT=60] Date&Time settings screen, toggle button title -->
- <string name="location_time_zone_detection_toggle_title">Use location</string>
+ <string name="location_time_zone_detection_toggle_title">Use location for time zone</string>
<!-- [CHAR LIMIT=50] Date&Time settings screen, title of the dialog when AutoTimeZone is degraded -->
<string name="location_time_zone_detection_status_title">Cannot set the time zone automatically</string>
<!-- Date&Time settings screen, summary of the dialog when AutoTimeZone is degraded by settings-->
@@ -3800,7 +3804,7 @@
<!-- [CHAR LIMIT=NONE] Location settings screen, summary when location time zone detection is not
applicable due to other settings like the "automatic time zone detection enabled" setting
being set to "off". -->
- <string name="location_time_zone_detection_auto_is_off">Automatic time zone is off</string>
+ <string name="location_time_zone_detection_auto_is_off">Not available when automatic time zone is off</string>
<!-- [CHAR LIMIT=NONE] Location settings screen, summary when location time zone detection is not
applicable for the user for unspecified reasons. More specific messages are used when there
is a clear reason. -->
@@ -3811,7 +3815,7 @@
allowed for the user, e.g. because of device policy -->
<string name="location_time_zone_detection_not_allowed">Location time zone detection changes are not allowed</string>
<!-- [CHAR LIMIT=NONE] Location settings screen, summary when location time zone detection is enabled. -->
- <string name="location_time_zone_detection_auto_is_on">Location may be used to set time zone</string>
+ <string name="location_time_zone_detection_auto_is_on">If your device location is available, it may be used to set your time zone</string>
<!-- Main settings screen, setting summary for the user to go into the About phone screen-->
<string name="about_settings_summary">View legal info, status, software version</string>
<!-- About phone settings screen, setting option name to go to dialog that shows legal info -->
@@ -8653,6 +8657,13 @@
<!-- Configure Notifications: Title for the option controlling notifications for work profile. [CHAR LIMIT=30] -->
<string name="locked_work_profile_notification_title">When work profile is locked</string>
+ <!-- Configure notifications: Title for the option controlling whether or not to minimalize the
+ number of notifications to show on the lock screen[CHAR LIMIT=60] -->
+ <string name="lock_screen_notif_minimalism">Lock screen notification minimalism</string>
+
+ <!-- Configure notifications: Summary for option lock_screen_notif_minimalism. [CHAR LIMIT=100] -->
+ <string name="lock_screen_notif_minimalism_summary">Show fewer notifications on lock screen</string>
+
<!-- Configure notifications: Title for the option controlling whether only new notifications are displayed to the user
on the lock screen [CHAR LIMIT=60] -->
<string name="unseen_notifs_lock_screen">Show only new notifications on lock screen</string>
@@ -9581,6 +9592,9 @@
<!-- Modes: Hint for the EditText for editing a mode's name [CHAR LIMIT=30] -->
<string name="zen_mode_edit_name_hint">Mode name</string>
+ <!-- Modes: Text shown above the list of icons in the mode editor. [CHAR LIMIT=40] -->
+ <string name="zen_mode_edit_choose_icon_title">Choose an icon</string>
+
<!-- Modes: Trigger title for modes of type SCHEDULE_CALENDAR. [CHAR LIMIT=30] -->
<string name="zen_mode_trigger_title_schedule_calendar">Calendar events</string>
<!-- Modes: Trigger title for modes of type BEDTIME. [CHAR LIMIT=30] -->
diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml
index b673a083..c88b7ba 100644
--- a/res/xml/configure_notification_settings.xml
+++ b/res/xml/configure_notification_settings.xml
@@ -136,8 +136,16 @@
/>
<SwitchPreferenceCompat
- android:key="lock_screen_show_only_unseen_notifs"
+ android:key="lock_screen_notif_minimalism"
android:order="19"
+ android:title="@string/lock_screen_notif_minimalism"
+ android:summary="@string/lock_screen_notif_minimalism_summary"
+ settings:controller="com.android.settings.notification.LockscreenNotificationMinimalismPreferenceController"
+ />
+
+ <SwitchPreferenceCompat
+ android:key="lock_screen_show_only_unseen_notifs"
+ android:order="20"
android:title="@string/unseen_notifs_lock_screen"
android:summary="@string/unseen_notifs_lock_screen_summary"
settings:controller="com.android.settings.notification.ShowOnlyUnseenNotificationsOnLockscreenPreferenceController"
@@ -146,7 +154,7 @@
<Preference
android:fragment="com.android.settings.accessibility.FlashNotificationsPreferenceFragment"
android:key="flash_notifications_preference"
- android:order="20"
+ android:order="21"
android:persistent="false"
android:title="@string/flash_notifications_title"
settings:searchable="false"
@@ -154,7 +162,7 @@
<com.android.settingslib.RestrictedPreference
android:key="app_and_notif_cell_broadcast_settings"
- android:order="21"
+ android:order="22"
android:title="@string/cell_broadcast_settings"
settings:useAdminDisabledSummary="true">
<intent
@@ -165,33 +173,33 @@
<SwitchPreferenceCompat
android:key="silent_icons"
- android:order="22"
+ android:order="23"
android:title="@string/silent_notifications_status_bar"
settings:controller="com.android.settings.notification.SilentStatusBarPreferenceController"/>
<SwitchPreferenceCompat
android:key="show_snooze_options"
- android:order="23"
+ android:order="24"
android:title="@string/snooze_options_title"
settings:controller="com.android.settings.notification.SnoozeNotificationPreferenceController" />
<!-- Notification badging -->
<SwitchPreferenceCompat
android:key="notification_badging"
- android:order="24"
+ android:order="25"
android:title="@string/notification_badging_title"
settings:controller="com.android.settings.notification.BadgingNotificationPreferenceController"/>
<!-- Pulse notification light, on devices that support it -->
<SwitchPreferenceCompat
android:key="notification_pulse"
- android:order="25"
+ android:order="26"
android:title="@string/notification_pulse_title"
settings:controller="com.android.settings.notification.PulseNotificationPreferenceController"/>
<SwitchPreferenceCompat
android:key="notification_assistant"
- android:order="26"
+ android:order="27"
android:title="@string/notification_assistant_title"
android:summary="@string/notification_assistant_summary"
settings:controller="com.android.settings.notification.NotificationAssistantPreferenceController"/>
diff --git a/res/xml/date_time_prefs.xml b/res/xml/date_time_prefs.xml
index 3fb4a06..d8643be 100644
--- a/res/xml/date_time_prefs.xml
+++ b/res/xml/date_time_prefs.xml
@@ -23,7 +23,7 @@
<com.android.settingslib.RestrictedSwitchPreference
android:key="auto_time"
android:title="@string/date_time_auto"
- settings:useAdditionalSummary="true"
+ android:summary="@string/summary_placeholder"
settings:userRestriction="no_config_date_time"
settings:controller="com.android.settings.datetime.AutoTimePreferenceController" />
@@ -48,6 +48,7 @@
<com.android.settingslib.RestrictedSwitchPreference
android:key="auto_zone"
android:title="@string/zone_auto_title"
+ android:summary="@string/summary_placeholder"
settings:userRestriction="no_config_date_time"
settings:controller="com.android.settings.datetime.AutoTimeZonePreferenceController" />
@@ -60,6 +61,7 @@
<SwitchPreferenceCompat
android:key="location_time_zone_detection"
android:title="@string/location_time_zone_detection_toggle_title"
+ android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.datetime.LocationTimeZoneDetectionPreferenceController"/>
<com.android.settingslib.RestrictedPreference
diff --git a/res/xml/modes_edit_name_icon.xml b/res/xml/modes_edit_name_icon.xml
index 2109c77..4bcf67d 100644
--- a/res/xml/modes_edit_name_icon.xml
+++ b/res/xml/modes_edit_name_icon.xml
@@ -33,13 +33,15 @@
android:key="name"
android:layout="@layout/modes_edit_name" />
- <com.android.settings.applications.SpacePreference
- android:layout_height="32dp" />
+ <PreferenceCategory
+ android:title="@string/zen_mode_edit_choose_icon_title"
+ android:key="modes_filters">
- <com.android.settingslib.widget.LayoutPreference
- android:key="icon_list"
- android:selectable="false"
- android:layout="@layout/modes_icon_list"/>
+ <com.android.settingslib.widget.LayoutPreference
+ android:key="icon_list"
+ android:selectable="false"
+ android:layout="@layout/modes_icon_list" />
+ </PreferenceCategory>
<com.android.settingslib.widget.LayoutPreference
android:key="done"
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 7e8969d..d0fe980 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -60,7 +60,7 @@
<Preference
android:key="zen_access"
- android:title="@string/manage_zen_access_title"
+ android:title="@string/manage_zen_modes_access_title"
android:order="-1500"
android:fragment="com.android.settings.notification.zen.ZenAccessSettings"
settings:controller="com.android.settings.applications.specialaccess.zenaccess.ZenAccessController" />
diff --git a/src/com/android/settings/SettingsInitialize.java b/src/com/android/settings/SettingsInitialize.java
index 4887e26..254ef8c 100644
--- a/src/com/android/settings/SettingsInitialize.java
+++ b/src/com/android/settings/SettingsInitialize.java
@@ -39,6 +39,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
+import com.android.settings.core.instrumentation.ElapsedTimeUtils;
import com.android.settings.homepage.DeepLinkHomepageActivity;
import com.android.settings.search.SearchStateReceiver;
import com.android.settingslib.utils.ThreadUtils;
@@ -69,6 +70,7 @@
webviewSettingSetup(context, pm, userInfo);
ThreadUtils.postOnBackgroundThread(() -> refreshExistingShortcuts(context));
enableTwoPaneDeepLinkActivityIfNecessary(pm, context);
+ storeSuwCompleteTimestamp(context, broadcast);
}
private void managedProfileSetup(Context context, final PackageManager pm, Intent broadcast,
@@ -161,4 +163,10 @@
pm.setComponentEnabledSetting(searchStateReceiver, enableState,
PackageManager.DONT_KILL_APP);
}
+
+ private void storeSuwCompleteTimestamp(Context context, Intent broadcast) {
+ if (SetupWizardUtils.ACTION_SETUP_WIZARD_FINISHED.equals(broadcast.getAction())) {
+ ElapsedTimeUtils.storeSuwFinishedTimestamp(context, System.currentTimeMillis());
+ }
+ }
}
diff --git a/src/com/android/settings/SetupWizardUtils.java b/src/com/android/settings/SetupWizardUtils.java
index 25e9159..57adeee 100644
--- a/src/com/android/settings/SetupWizardUtils.java
+++ b/src/com/android/settings/SetupWizardUtils.java
@@ -32,6 +32,9 @@
public class SetupWizardUtils {
+ public static final String ACTION_SETUP_WIZARD_FINISHED =
+ "com.google.android.setupwizard.SETUP_WIZARD_FINISHED";
+
public static String getThemeString(Intent intent) {
String theme = intent.getStringExtra(WizardManagerHelper.EXTRA_THEME);
if (theme == null) {
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index 90d733e..1712e85 100644
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -715,7 +715,7 @@
if (mPackageName == null) {
final Intent intent = args == null ?
getActivity().getIntent() : (Intent) args.getParcelable("intent");
- if (intent != null) {
+ if (intent != null && intent.getData() != null) {
mPackageName = intent.getData().getSchemeSpecificPart();
}
}
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 6c16d94..b837e1e 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -1034,6 +1034,9 @@
}
private void autoSetCollapsingToolbarLayoutScrolling() {
+ if (mAppBarLayout == null) {
+ return;
+ }
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
diff --git a/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt b/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
index e3233ed..761a9c3 100644
--- a/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
@@ -58,6 +58,7 @@
import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.UserInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
@@ -67,6 +68,7 @@
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RemoveFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RenameFingerprintInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.UserInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
import java.util.concurrent.Executors
import kotlinx.coroutines.MainScope
@@ -97,11 +99,11 @@
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser
)
)
- private val fingerprintEnrollmentRepository =
- FingerprintEnrollmentRepositoryImpl(fingerprintManager, userRepo, fingerprintSettingsRepository,
- backgroundDispatcher, applicationScope)
private val fingerprintSensorRepository: FingerprintSensorRepository =
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, applicationScope)
+ private val fingerprintEnrollmentRepository =
+ FingerprintEnrollmentRepositoryImpl(fingerprintManager, userRepo, fingerprintSettingsRepository,
+ backgroundDispatcher, applicationScope, fingerprintSensorRepository)
private val debuggingRepository: DebuggingRepository = DebuggingRepositoryImpl()
private val udfpsDebugRepo = UdfpsEnrollDebugRepositoryImpl()
@@ -118,11 +120,13 @@
EnrollFingerprintInteractorImpl(context.userId, fingerprintManager, Settings)
fun createFingerprintsEnrolledInteractor(): EnrolledFingerprintsInteractorImpl =
- EnrolledFingerprintsInteractorImpl(fingerprintManager, context.userId)
+ EnrolledFingerprintsInteractorImpl(fingerprintEnrollmentRepository)
fun createAuthenticateInteractor(): AuthenitcateInteractor =
AuthenticateInteractorImpl(fingerprintManager, context.userId)
+ fun createUserInteractor(): UserInteractor = UserInteractorImpl(userRepo)
+
fun createRemoveFingerprintInteractor(): RemoveFingerprintInteractor =
RemoveFingerprintsInteractorImpl(fingerprintManager, context.userId)
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt
index 22904e9..0bb4eea 100644
--- a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt
@@ -23,14 +23,16 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.withContext
/** Repository that contains information about fingerprint enrollments. */
@@ -38,20 +40,31 @@
/** The current enrollments of the user */
val currentEnrollments: Flow<List<FingerprintData>?>
+ /** Indicates the maximum fingerprints that are enrollable * */
+ val maxFingerprintsEnrollable: Flow<Int>
+
/** Indicates if a user can enroll another fingerprint */
val canEnrollUser: Flow<Boolean>
- fun maxFingerprintsEnrollable(): Int
+ /**
+ * Indicates if we should use the default settings for maximum enrollments or the sensor props
+ * from the fingerprint sensor
+ */
+ fun setShouldUseSettingsMaxFingerprints(useSettings: Boolean)
}
class FingerprintEnrollmentRepositoryImpl(
- fingerprintManager: FingerprintManager,
+ private val fingerprintManager: FingerprintManager,
userRepo: UserRepo,
- private val settingsRepository: FingerprintSettingsRepository,
+ settingsRepository: FingerprintSettingsRepository,
backgroundDispatcher: CoroutineDispatcher,
applicationScope: CoroutineScope,
+ sensorRepo: FingerprintSensorRepository,
) : FingerprintEnrollmentRepository {
+ private val _shouldUseSettingsMaxFingerprints = MutableStateFlow(false)
+ val shouldUseSettingsMaxFingerprints = _shouldUseSettingsMaxFingerprints.asStateFlow()
+
private val enrollmentChangedFlow: Flow<Int?> =
callbackFlow {
val callback =
@@ -72,27 +85,34 @@
override val currentEnrollments: Flow<List<FingerprintData>> =
userRepo.currentUser
.distinctUntilChanged()
- .flatMapLatest { currentUser ->
- enrollmentChangedFlow.map { enrollmentChanged ->
- if (enrollmentChanged == null || enrollmentChanged == currentUser) {
- fingerprintManager
- .getEnrolledFingerprints(currentUser)
- ?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }
- ?.toList()
- } else {
- null
- }
- }
- }
+ .combine(enrollmentChangedFlow) { currentUser, _ -> getFingerprintsForUser(currentUser) }
.filterNotNull()
.flowOn(backgroundDispatcher)
- override val canEnrollUser: Flow<Boolean> =
- currentEnrollments.map {
- it?.size?.let { it < settingsRepository.maxEnrollableFingerprints() } ?: false
+ override val maxFingerprintsEnrollable: Flow<Int> =
+ shouldUseSettingsMaxFingerprints.combine(sensorRepo.fingerprintSensor) {
+ shouldUseSettings,
+ sensor ->
+ if (shouldUseSettings) {
+ settingsRepository.maxEnrollableFingerprints()
+ } else {
+ sensor.maxEnrollmentsPerUser
+ }
}
- override fun maxFingerprintsEnrollable(): Int {
- return settingsRepository.maxEnrollableFingerprints()
+ override val canEnrollUser: Flow<Boolean> =
+ currentEnrollments.combine(maxFingerprintsEnrollable) { enrollments, maxFingerprints ->
+ enrollments.size < maxFingerprints
+ }
+
+ override fun setShouldUseSettingsMaxFingerprints(useSettings: Boolean) {
+ _shouldUseSettingsMaxFingerprints.update { useSettings }
+ }
+
+ private fun getFingerprintsForUser(userId: Int): List<FingerprintData>? {
+ return fingerprintManager
+ .getEnrolledFingerprints(userId)
+ ?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }
+ ?.toList()
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/UserRepo.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/UserRepo.kt
index 720e778..9126043 100644
--- a/src/com/android/settings/biometrics/fingerprint2/data/repository/UserRepo.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/UserRepo.kt
@@ -17,7 +17,10 @@
package com.android.settings.biometrics.fingerprint2.data.repository
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.update
/**
* A repository responsible for indicating the current user.
@@ -27,8 +30,18 @@
* This flow indicates the current user.
*/
val currentUser: Flow<Int>
+
+ /**
+ * Updates the current user.
+ */
+ fun updateUser(user: Int)
}
-class UserRepoImpl(val currUser: Int): UserRepo {
- override val currentUser: Flow<Int> = flowOf(currUser)
+class UserRepoImpl(currUser: Int): UserRepo {
+ private val _currentUser = MutableStateFlow(currUser)
+ override val currentUser = _currentUser.asStateFlow()
+
+ override fun updateUser(user: Int) {
+ _currentUser.update { user }
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/CanEnrollFingerprintsInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/CanEnrollFingerprintsInteractorImpl.kt
index caeea4e..cfdfbe2 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/CanEnrollFingerprintsInteractorImpl.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/CanEnrollFingerprintsInteractorImpl.kt
@@ -21,11 +21,14 @@
import kotlinx.coroutines.flow.Flow
class CanEnrollFingerprintsInteractorImpl(
- val fingerprintEnrollmentRepository: FingerprintEnrollmentRepository
+ private val fingerprintEnrollmentRepository: FingerprintEnrollmentRepository
) : CanEnrollFingerprintsInteractor {
override val canEnrollFingerprints: Flow<Boolean> = fingerprintEnrollmentRepository.canEnrollUser
/** Indicates the maximum fingerprints enrollable for a given user */
- override fun maxFingerprintsEnrollable(): Int {
- return fingerprintEnrollmentRepository.maxFingerprintsEnrollable()
+ override val maxFingerprintsEnrollable: Flow<Int> =
+ fingerprintEnrollmentRepository.maxFingerprintsEnrollable
+
+ override fun setShouldUseSettingsMaxFingerprints(useSettings: Boolean) {
+ fingerprintEnrollmentRepository.setShouldUseSettingsMaxFingerprints(useSettings)
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrolledFingerprintsInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrolledFingerprintsInteractorImpl.kt
index 83b532e..f8bcaf7 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrolledFingerprintsInteractorImpl.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrolledFingerprintsInteractorImpl.kt
@@ -16,22 +16,14 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor
-import android.hardware.fingerprint.FingerprintManager
+import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepository
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flow
class EnrolledFingerprintsInteractorImpl(
- private val fingerprintManager: FingerprintManager,
- userId: Int,
+ private val fingerprintEnrollmentRepository: FingerprintEnrollmentRepository
) : EnrolledFingerprintsInteractor {
- override val enrolledFingerprints: Flow<List<FingerprintData>?> = flow {
- emit(
- fingerprintManager
- .getEnrolledFingerprints(userId)
- ?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }
- ?.toList()
- )
- }
+ override val enrolledFingerprints: Flow<List<FingerprintData>?> =
+ fingerprintEnrollmentRepository.currentEnrollments
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UserInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UserInteractorImpl.kt
new file mode 100644
index 0000000..506006e
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UserInteractorImpl.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint2.domain.interactor
+
+import com.android.settings.biometrics.fingerprint2.data.repository.UserRepo
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.UserInteractor
+import kotlinx.coroutines.flow.Flow
+
+class UserInteractorImpl(private val userRepo: UserRepo) : UserInteractor {
+ override val currentUser: Flow<Int> = userRepo.currentUser
+
+ override fun updateUser(user: Int) = userRepo.updateUser(user)
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/CanEnrollFingerprintsInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/CanEnrollFingerprintsInteractor.kt
index 11a9258..a5277a5 100644
--- a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/CanEnrollFingerprintsInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/CanEnrollFingerprintsInteractor.kt
@@ -23,5 +23,17 @@
/** Returns true if a user can enroll a fingerprint false otherwise. */
val canEnrollFingerprints: Flow<Boolean>
/** Indicates the maximum fingerprints enrollable for a given user */
- fun maxFingerprintsEnrollable(): Int
+ val maxFingerprintsEnrollable: Flow<Int>
+
+ /**
+ * Indicates if we should use the default settings for maximum enrollments or the sensor props
+ * from the fingerprint sensor. This can be useful if you are supporting HIDL & AIDL enrollment
+ * types from one code base. Prior to AIDL there was no way to determine how many
+ * fingerprints were enrollable, Settings relied on
+ * com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser.
+ *
+ * Typically Fingerprints with AIDL HAL's should not use this
+ * (setShouldUseSettingsMaxFingerprints(false))
+ */
+ fun setShouldUseSettingsMaxFingerprints(useSettings: Boolean)
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/UserInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/UserInteractor.kt
new file mode 100644
index 0000000..17b147a
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/UserInteractor.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint2.lib.domain.interactor
+
+import kotlinx.coroutines.flow.Flow
+
+interface UserInteractor {
+ /**
+ * This flow indicates the current user.
+ */
+ val currentUser: Flow<Int>
+
+ /**
+ * Updates the current user.
+ */
+ fun updateUser(user: Int)
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsViewModel.kt
index c306c78..7aad16d 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsViewModel.kt
@@ -43,7 +43,6 @@
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.sample
-import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@@ -72,10 +71,12 @@
/** Represents the stream of the information of "Add Fingerprint" preference. */
val addFingerprintPrefInfo: Flow<Pair<Boolean, Int>> =
- _enrolledFingerprints.filterOnlyWhenSettingsIsShown().combine(
- canEnrollFingerprintsInteractor.canEnrollFingerprints
- ) { _, canEnrollFingerprints ->
- Pair(canEnrollFingerprints, canEnrollFingerprintsInteractor.maxFingerprintsEnrollable())
+ combine(
+ _enrolledFingerprints.filterOnlyWhenSettingsIsShown(),
+ canEnrollFingerprintsInteractor.canEnrollFingerprints,
+ canEnrollFingerprintsInteractor.maxFingerprintsEnrollable,
+ ) { _, canEnrollFingerprints, maxFingerprints ->
+ Pair(canEnrollFingerprints, maxFingerprints)
}
/** Represents the stream of visibility of sfps preference. */
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
index 2c65934..387bf83 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
@@ -31,15 +31,12 @@
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.View;
-import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
@@ -71,8 +68,9 @@
private volatile BluetoothDevice mJustBonded = null;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+ @VisibleForTesting
@Nullable
- private AlertDialog mProgressDialog = null;
+ ProgressDialogFragment mProgressDialog = null;
@VisibleForTesting
boolean mShouldTriggerAudioSharingShareThenPairFlow = false;
private CopyOnWriteArrayList<BluetoothDevice> mDevicesWithMetadataChangedListener =
@@ -384,41 +382,24 @@
finish();
}
- // TODO: use DialogFragment
private void showConnectingDialog(@NonNull String deviceName) {
postOnMainThread(() -> {
String message = getContext().getString(R.string.progress_dialog_connect_device_content,
deviceName);
+ if (mProgressDialog == null) {
+ mProgressDialog = ProgressDialogFragment.newInstance(this);
+ }
if (mProgressDialog != null) {
- Log.d(getLogTag(), "showConnectingDialog, is already showing");
- TextView textView = mProgressDialog.findViewById(R.id.message);
- if (textView != null && !message.equals(textView.getText().toString())) {
- Log.d(getLogTag(), "showConnectingDialog, update message");
- textView.setText(message);
- }
- return;
+ mProgressDialog.show(message);
}
- Log.d(getLogTag(), "showConnectingDialog, show dialog");
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- LayoutInflater inflater = LayoutInflater.from(builder.getContext());
- View customView = inflater.inflate(
- R.layout.dialog_audio_sharing_progress, /* root= */
- null);
- TextView textView = customView.findViewById(R.id.message);
- if (textView != null) {
- textView.setText(message);
- }
- AlertDialog dialog = builder.setView(customView).setCancelable(false).create();
- dialog.setCanceledOnTouchOutside(false);
- mProgressDialog = dialog;
- dialog.show();
});
}
private void dismissConnectingDialog() {
postOnMainThread(() -> {
if (mProgressDialog != null) {
- mProgressDialog.dismiss();
+ Log.d(getLogTag(), "Dismiss connecting dialog.");
+ mProgressDialog.dismissAllowingStateLoss();
}
});
}
diff --git a/src/com/android/settings/bluetooth/ProgressDialogFragment.java b/src/com/android/settings/bluetooth/ProgressDialogFragment.java
new file mode 100644
index 0000000..15d5329
--- /dev/null
+++ b/src/com/android/settings/bluetooth/ProgressDialogFragment.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+import com.google.common.base.Strings;
+
+public class ProgressDialogFragment extends InstrumentedDialogFragment {
+ private static final String TAG = "BTProgressDialog";
+
+ private static final String BUNDLE_KEY_MESSAGE = "bundle_key_message";
+
+ @Nullable private static FragmentManager sManager;
+ @Nullable private static Lifecycle sLifecycle;
+ private String mMessage = "";
+ @Nullable private AlertDialog mAlertDialog;
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO: add metrics
+ return 0;
+ }
+
+ /**
+ * Returns a new instance of {@link ProgressDialogFragment} dialog.
+ *
+ * @param host The Fragment this dialog will be hosted.
+ */
+ @Nullable
+ public static ProgressDialogFragment newInstance(@Nullable Fragment host) {
+ if (host == null) return null;
+ try {
+ sManager = host.getChildFragmentManager();
+ sLifecycle = host.getLifecycle();
+ } catch (IllegalStateException e) {
+ Log.d(TAG, "Fail to create new instance: " + e.getMessage());
+ return null;
+ }
+ return new ProgressDialogFragment();
+ }
+
+ /**
+ * Display {@link ProgressDialogFragment} dialog.
+ *
+ * @param message The message to be shown on the dialog
+ */
+ public void show(@NonNull String message) {
+ if (sManager == null) return;
+ Lifecycle.State currentState = sLifecycle == null ? null : sLifecycle.getCurrentState();
+ if (currentState == null || !currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ Log.d(TAG, "Fail to show dialog with state: " + currentState);
+ return;
+ }
+ if (mAlertDialog != null && mAlertDialog.isShowing()) {
+ if (!mMessage.equals(message)) {
+ Log.d(TAG, "Update dialog message.");
+ TextView messageView = mAlertDialog.findViewById(R.id.message);
+ if (messageView != null) {
+ messageView.setText(message);
+ }
+ mMessage = message;
+ }
+ Log.d(TAG, "Dialog is showing, return.");
+ return;
+ }
+ mMessage = message;
+ Log.d(TAG, "Show up the progress dialog.");
+ Bundle args = new Bundle();
+ args.putString(BUNDLE_KEY_MESSAGE, message);
+ setArguments(args);
+ show(sManager, TAG);
+ }
+
+ /** Returns the current message on the dialog. */
+ @VisibleForTesting
+ @NonNull
+ public String getMessage() {
+ return mMessage;
+ }
+
+ private ProgressDialogFragment() {
+ }
+
+ @Override
+ @NonNull
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Bundle args = requireArguments();
+ String message = args.getString(BUNDLE_KEY_MESSAGE, "");
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+ View customView = inflater.inflate(
+ R.layout.dialog_audio_sharing_progress, /* root= */ null);
+ TextView textView = customView.findViewById(R.id.message);
+ if (textView != null && !Strings.isNullOrEmpty(message)) {
+ textView.setText(message);
+ }
+ AlertDialog dialog = builder.setView(customView).setCancelable(false).create();
+ dialog.setCanceledOnTouchOutside(false);
+ mAlertDialog = dialog;
+ return dialog;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt b/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt
index f6e6f16..b16bff1 100644
--- a/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt
+++ b/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt
@@ -16,6 +16,7 @@
package com.android.settings.bluetooth.ui.model
+import android.content.Intent
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
@@ -31,7 +32,7 @@
val title: String,
val summary: String? = null,
val icon: DeviceSettingIcon? = null,
- val onClick: (() -> Unit)? = null,
+ val intent: Intent? = null,
) : DeviceSettingPreferenceModel
/** Models a switch preference. */
@@ -42,7 +43,7 @@
val icon: DeviceSettingIcon? = null,
val checked: Boolean,
val onCheckedChange: ((Boolean) -> Unit),
- val onPrimaryClick: (() -> Unit)? = null,
+ val intent: Intent? = null,
) : DeviceSettingPreferenceModel
/** Models a multi-toggle preference. */
@@ -71,6 +72,6 @@
data class HelpPreference(
@DeviceSettingId override val id: Int,
val icon: DeviceSettingIcon,
- val onClick: (() -> Unit),
+ val intent: Intent,
) : DeviceSettingPreferenceModel
}
diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
index a5997e7..ecd700b 100644
--- a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
+++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
@@ -18,6 +18,7 @@
import android.bluetooth.BluetoothAdapter
import android.content.Context
+import android.content.Intent
import android.media.AudioManager
import android.os.Bundle
import androidx.compose.animation.AnimatedVisibility
@@ -101,13 +102,13 @@
) : DeviceDetailsFragmentFormatter {
private val repository =
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
- context,
+ fragment.requireActivity().application,
bluetoothAdapter,
fragment.lifecycleScope,
)
private val spatialAudioInteractor =
featureFactory.bluetoothFeatureProvider.getSpatialAudioInteractor(
- context,
+ fragment.requireActivity().application,
context.getSystemService(AudioManager::class.java),
fragment.lifecycleScope,
)
@@ -312,10 +313,10 @@
return { deviceSettingIcon(model.icon) }
}
}
- if (model.onPrimaryClick != null) {
+ if (model.intent != null) {
TwoTargetSwitchPreference(
switchPrefModel,
- primaryOnClick = model.onPrimaryClick::invoke,
+ primaryOnClick = { startActivity(model.intent) },
)
} else {
SwitchPreference(switchPrefModel)
@@ -329,7 +330,7 @@
override val title = model.title
override val summary = { model.summary ?: "" }
override val onClick = {
- model.onClick?.invoke()
+ model.intent?.let { startActivity(it) }
Unit
}
override val icon: (@Composable () -> Unit)?
@@ -361,7 +362,12 @@
)
.launch()
}
- override val icon = @Composable { deviceSettingIcon(null) }
+ override val icon =
+ @Composable {
+ deviceSettingIcon(
+ DeviceSettingIcon.ResourceIcon(R.drawable.ic_chevron_right_24dp)
+ )
+ }
}
)
}
@@ -376,6 +382,11 @@
icon?.let { Icon(it, modifier = Modifier.size(SettingsDimension.itemIconSize)) }
}
+ private fun startActivity(intent: Intent) {
+ intent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(intent)
+ }
+
private fun getPreferenceKey(settingId: Int) = "DEVICE_SETTING_${settingId}"
companion object {
diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt
index 885516e..7cb1c0d 100644
--- a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt
+++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt
@@ -16,9 +16,11 @@
package com.android.settings.bluetooth.ui.view
+import android.app.settings.SettingsEnums
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.content.Context
+import android.content.Intent
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.Menu
@@ -49,8 +51,7 @@
private lateinit var cachedDevice: CachedBluetoothDevice
private lateinit var helpItem: StateFlow<DeviceSettingPreferenceModel.HelpPreference?>
- // TODO(b/343317785): add metrics category
- override fun getMetricsCategory(): Int = 0
+ override fun getMetricsCategory(): Int = SettingsEnums.BLUETOOTH_DEVICE_DETAILS_MORE_SETTINGS
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
@@ -74,7 +75,10 @@
override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
if (menuItem.itemId == MENU_HELP_ITEM_ID) {
- helpItem.value?.let { it.onClick() }
+ helpItem.value?.intent?.let {
+ it.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ requireContext().startActivity(it)
+ }
return true
}
return super.onOptionsItemSelected(menuItem)
diff --git a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt
index 67a0ebc..fe66cb5 100644
--- a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt
+++ b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt
@@ -101,7 +101,7 @@
DeviceSettingStateModel.ActionSwitchPreferenceState(newState)
)
},
- onPrimaryClick = { intent?.let { application.startActivity(it) } },
+ intent = intent,
)
} else {
DeviceSettingPreferenceModel.PlainPreference(
@@ -109,7 +109,7 @@
title = title,
summary = summary,
icon = icon,
- onClick = { intent?.let { application.startActivity(it) } },
+ intent = intent,
)
}
}
@@ -119,7 +119,7 @@
DeviceSettingPreferenceModel.HelpPreference(
id = id,
icon = DeviceSettingIcon.ResourceIcon(R.drawable.ic_help),
- onClick = { application.startActivity(intent) },
+ intent = intent,
)
is DeviceSettingModel.MultiTogglePreference ->
DeviceSettingPreferenceModel.MultiTogglePreference(
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
index 1b68eac..54a758c 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
@@ -31,6 +31,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.bluetooth.BluetoothPairingDetail;
@@ -95,6 +96,11 @@
Log.d(TAG, "Fail to show dialog: " + e.getMessage());
return;
}
+ Lifecycle.State currentState = host.getLifecycle().getCurrentState();
+ if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ Log.d(TAG, "Fail to show dialog with state: " + currentState);
+ return;
+ }
sHost = host;
sListener = listener;
sEventData = eventData;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
index 7d91644..fbd2e63 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
@@ -28,6 +28,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -92,6 +93,11 @@
Log.d(TAG, "Fail to show dialog: " + e.getMessage());
return;
}
+ Lifecycle.State currentState = host.getLifecycle().getCurrentState();
+ if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ Log.d(TAG, "Fail to show dialog with state: " + currentState);
+ return;
+ }
AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
if (dialog != null) {
int newGroupId = BluetoothUtils.getGroupId(newDevice);
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragment.java
index e842b37..94d4a69 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragment.java
@@ -25,6 +25,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -53,6 +54,11 @@
Log.d(TAG, "Fail to show dialog: " + e.getMessage());
return;
}
+ Lifecycle.State currentState = host.getLifecycle().getCurrentState();
+ if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ Log.d(TAG, "Fail to show dialog with state: " + currentState);
+ return;
+ }
AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
if (dialog != null) {
Log.d(TAG, "Dialog is showing, return.");
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingIncompatibleDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingIncompatibleDialogFragment.java
index a8ad70b..e8ab716 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingIncompatibleDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingIncompatibleDialogFragment.java
@@ -26,6 +26,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -69,6 +70,11 @@
Log.d(TAG, "Fail to show dialog: " + e.getMessage());
return;
}
+ Lifecycle.State currentState = host.getLifecycle().getCurrentState();
+ if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ Log.d(TAG, "Fail to show dialog with state: " + currentState);
+ return;
+ }
sListener = listener;
AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
if (dialog != null) {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
index ef461eb..a952c48 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
@@ -28,6 +28,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
@@ -89,6 +90,11 @@
Log.d(TAG, "Fail to show dialog: " + e.getMessage());
return;
}
+ Lifecycle.State currentState = host.getLifecycle().getCurrentState();
+ if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ Log.d(TAG, "Fail to show dialog with state: " + currentState);
+ return;
+ }
sListener = listener;
sNewDevice = newDevice;
sEventData = eventData;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragment.java
index 53bfcf8..840c7bb 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragment.java
@@ -31,6 +31,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -72,6 +73,11 @@
Log.d(TAG, "Fail to show dialog: " + e.getMessage());
return;
}
+ Lifecycle.State currentState = host.getLifecycle().getCurrentState();
+ if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ Log.d(TAG, "Fail to show dialog with state: " + currentState);
+ return;
+ }
AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
if (dialog != null) {
if (!sMessage.equals(message)) {
@@ -80,6 +86,7 @@
if (messageView != null) {
messageView.setText(message);
}
+ sMessage = message;
}
Log.d(TAG, "Dialog is showing, return.");
return;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
index 5b71f51..2bd79c9 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
@@ -28,6 +28,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -89,6 +90,11 @@
Log.d(TAG, "Fail to show dialog: " + e.getMessage());
return;
}
+ Lifecycle.State currentState = host.getLifecycle().getCurrentState();
+ if (!currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ Log.d(TAG, "Fail to show dialog with state: " + currentState);
+ return;
+ }
AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
if (dialog != null) {
int newGroupId = BluetoothUtils.getGroupId(newDevice);
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index b91a1f1..14da750 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -767,7 +767,7 @@
&& !(fragment instanceof AudioSharingErrorDialogFragment)
&& ((DialogFragment) fragment).getDialog() != null) {
Log.d(TAG, "Remove stale dialog = " + fragment.getTag());
- ((DialogFragment) fragment).dismiss();
+ ((DialogFragment) fragment).dismissAllowingStateLoss();
}
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
index d5be2bb..d1af8d9 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
@@ -106,7 +106,7 @@
// If the initial volume from `onDeviceVolumeChanged` is larger than zero (not muted), we will
// override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
private final AtomicInteger mLatestPositiveVolume = new AtomicInteger(25);
- private final AtomicBoolean mHasStopped = new AtomicBoolean(false);
+ private final Object mLocalSessionLock = new Object();
private int mBroadcastId;
@Nullable private List<BluetoothDevice> mDevices;
@Nullable private LocalBluetoothManager mLocalBtManager;
@@ -125,7 +125,7 @@
if (!BluetoothUtils.isAudioSharingEnabled()) {
return;
}
-
+ Log.d(TAG, "onCreate()");
super.onCreate();
mLocalBtManager = Utils.getLocalBtManager(this);
if (mLocalBtManager == null) {
@@ -146,26 +146,35 @@
return;
}
- if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
- NotificationChannel notificationChannel =
- new NotificationChannel(
- CHANNEL_ID,
- getString(com.android.settings.R.string.bluetooth),
- NotificationManager.IMPORTANCE_HIGH);
- mNotificationManager.createNotificationChannel(notificationChannel);
- }
+ mExecutor.execute(
+ () -> {
+ if (mLocalBtManager == null
+ || mLeBroadcastAssistant == null
+ || mNotificationManager == null) {
+ return;
+ }
+ if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
+ NotificationChannel notificationChannel =
+ new NotificationChannel(
+ CHANNEL_ID,
+ getString(com.android.settings.R.string.bluetooth),
+ NotificationManager.IMPORTANCE_HIGH);
+ mNotificationManager.createNotificationChannel(notificationChannel);
+ }
- mBluetoothCallback = new BtCallback();
- mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback);
+ mBluetoothCallback = new BtCallback();
+ mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback);
- mVolumeControl = mLocalBtManager.getProfileManager().getVolumeControlProfile();
- if (mVolumeControl != null) {
- mVolumeControlCallback = new VolumeControlCallback();
- mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback);
- }
+ mVolumeControl = mLocalBtManager.getProfileManager().getVolumeControlProfile();
+ if (mVolumeControl != null) {
+ mVolumeControlCallback = new VolumeControlCallback();
+ mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback);
+ }
- mBroadcastAssistantCallback = new AssistantCallback();
- mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
+ mBroadcastAssistantCallback = new AssistantCallback();
+ mLeBroadcastAssistant.registerServiceCallBack(
+ mExecutor, mBroadcastAssistantCallback);
+ });
}
@Override
@@ -175,19 +184,29 @@
if (!BluetoothUtils.isAudioSharingEnabled()) {
return;
}
- if (mLocalBtManager != null) {
- mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
+ if (mDevices != null) {
+ mDevices.clear();
+ mDevices = null;
}
- if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) {
- mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
+ synchronized (mLocalSessionLock) {
+ if (mLocalSession != null) {
+ mLocalSession.release();
+ mLocalSession = null;
+ }
}
- if (mVolumeControl != null && mVolumeControlCallback != null) {
- mVolumeControl.unregisterCallback(mVolumeControlCallback);
- }
- if (mLocalSession != null) {
- mLocalSession.release();
- mLocalSession = null;
- }
+ mExecutor.execute(
+ () -> {
+ if (mLocalBtManager != null) {
+ mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
+ }
+ if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) {
+ mLeBroadcastAssistant.unregisterServiceCallBack(
+ mBroadcastAssistantCallback);
+ }
+ if (mVolumeControl != null && mVolumeControlCallback != null) {
+ mVolumeControl.unregisterCallback(mVolumeControlCallback);
+ }
+ });
}
@Override
@@ -195,43 +214,45 @@
Log.d(TAG, "onStartCommand()");
if (intent == null) {
Log.w(TAG, "Intent is null. Service will not start.");
- mHasStopped.set(true);
stopSelf();
return START_NOT_STICKY;
}
mBroadcastId = intent.getIntExtra(BROADCAST_ID, -1);
if (mBroadcastId == -1) {
Log.w(TAG, "Invalid broadcast ID. Service will not start.");
- mHasStopped.set(true);
stopSelf();
return START_NOT_STICKY;
}
var extra = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class);
if (extra == null || extra.isEmpty()) {
Log.w(TAG, "No device. Service will not start.");
- mHasStopped.set(true);
stopSelf();
return START_NOT_STICKY;
}
mDevices = Collections.synchronizedList(extra);
- createLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
- startForeground(NOTIFICATION_ID, buildNotification());
- // Reset in case the service is previously stopped but not yet destroyed.
- mHasStopped.set(false);
+ MediaSession.Token token =
+ getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
+ startForeground(NOTIFICATION_ID, buildNotification(token));
return START_NOT_STICKY;
}
- private void createLocalMediaSession(String title) {
- mLocalSession = new MediaSession(this, TAG);
- mLocalSession.setMetadata(
- new MediaMetadata.Builder()
- .putString(MediaMetadata.METADATA_KEY_TITLE, title)
- .putLong(MediaMetadata.METADATA_KEY_DURATION, STATIC_PLAYBACK_DURATION)
- .build());
- mLocalSession.setActive(true);
- mLocalSession.setPlaybackState(getPlaybackState());
- mMediaSessionCallback = new MediaSessionCallback();
- mLocalSession.setCallback(mMediaSessionCallback);
+ private MediaSession.Token getOrCreateLocalMediaSession(String title) {
+ synchronized (mLocalSessionLock) {
+ if (mLocalSession != null) {
+ return mLocalSession.getSessionToken();
+ }
+ mLocalSession = new MediaSession(this, TAG);
+ mLocalSession.setMetadata(
+ new MediaMetadata.Builder()
+ .putString(MediaMetadata.METADATA_KEY_TITLE, title)
+ .putLong(MediaMetadata.METADATA_KEY_DURATION, STATIC_PLAYBACK_DURATION)
+ .build());
+ mLocalSession.setActive(true);
+ mLocalSession.setPlaybackState(getPlaybackState());
+ mMediaSessionCallback = new MediaSessionCallback();
+ mLocalSession.setCallback(mMediaSessionCallback);
+ return mLocalSession.getSessionToken();
+ }
}
private PlaybackState getPlaybackState() {
@@ -252,12 +273,9 @@
return device != null ? device.getName() : DEFAULT_DEVICE_NAME;
}
- private Notification buildNotification() {
+ private Notification buildNotification(MediaSession.Token token) {
String deviceName = getDeviceName();
- Notification.MediaStyle mediaStyle =
- new Notification.MediaStyle()
- .setMediaSession(
- mLocalSession != null ? mLocalSession.getSessionToken() : null);
+ Notification.MediaStyle mediaStyle = new Notification.MediaStyle().setMediaSession(token);
if (deviceName != null && !deviceName.isEmpty()) {
mediaStyle.setRemotePlaybackInfo(
deviceName, com.android.settingslib.R.drawable.ic_bt_le_audio, null);
@@ -291,20 +309,15 @@
}
private void handleRemoveSource() {
- var unused =
- ThreadUtils.postOnBackgroundThread(
- () -> {
- List<BluetoothLeBroadcastReceiveState> connected =
- mAudioStreamsHelper == null
- ? emptyList()
- : mAudioStreamsHelper.getAllConnectedSources();
- if (connected.stream()
- .map(BluetoothLeBroadcastReceiveState::getBroadcastId)
- .noneMatch(id -> id == mBroadcastId)) {
- mHasStopped.set(true);
- stopSelf();
- }
- });
+ List<BluetoothLeBroadcastReceiveState> connected =
+ mAudioStreamsHelper == null
+ ? emptyList()
+ : mAudioStreamsHelper.getAllConnectedSources();
+ if (connected.stream()
+ .map(BluetoothLeBroadcastReceiveState::getBroadcastId)
+ .noneMatch(id -> id == mBroadcastId)) {
+ stopSelf();
+ }
}
}
@@ -326,7 +339,11 @@
mIsMuted.set(false);
mLatestPositiveVolume.set(volume);
}
- updateNotification(getPlaybackState());
+ synchronized (mLocalSessionLock) {
+ if (mLocalSession != null) {
+ mLocalSession.setPlaybackState(getPlaybackState());
+ }
+ }
}
}
}
@@ -336,7 +353,6 @@
public void onBluetoothStateChanged(int bluetoothState) {
if (BluetoothAdapter.STATE_OFF == bluetoothState) {
Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
- mHasStopped.set(true);
stopSelf();
}
}
@@ -362,7 +378,6 @@
}
if (mDevices == null || mDevices.isEmpty()) {
Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
- mHasStopped.set(true);
stopSelf();
}
}
@@ -371,7 +386,11 @@
private class MediaSessionCallback extends MediaSession.Callback {
public void onSeekTo(long pos) {
Log.d(TAG, "onSeekTo: " + pos);
- updateNotification(getPlaybackState());
+ synchronized (mLocalSessionLock) {
+ if (mLocalSession != null) {
+ mLocalSession.setPlaybackState(getPlaybackState());
+ }
+ }
}
@Override
@@ -425,18 +444,4 @@
});
}
}
-
- private void updateNotification(PlaybackState playbackState) {
- var unused =
- ThreadUtils.postOnBackgroundThread(
- () -> {
- if (mLocalSession != null) {
- mLocalSession.setPlaybackState(playbackState);
- if (mNotificationManager != null && !mHasStopped.get()) {
- mNotificationManager.notify(
- NOTIFICATION_ID, buildNotification());
- }
- }
- });
- }
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
index c0d9162..7c1281f 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
@@ -139,7 +139,6 @@
}
/** Retrieves a list of all LE broadcast receive states from active sinks. */
- @VisibleForTesting
public List<BluetoothLeBroadcastReceiveState> getAllConnectedSources() {
if (mLeBroadcastAssistant == null) {
Log.w(TAG, "getAllSources(): LeBroadcastAssistant is null!");
@@ -165,7 +164,6 @@
}
/** Retrieves LocalBluetoothLeBroadcastAssistant. */
- @VisibleForTesting
@Nullable
public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() {
return mLeBroadcastAssistant;
diff --git a/src/com/android/settings/datetime/AutoTimePreferenceController.java b/src/com/android/settings/datetime/AutoTimePreferenceController.java
index 2942acb..5b17e7e 100644
--- a/src/com/android/settings/datetime/AutoTimePreferenceController.java
+++ b/src/com/android/settings/datetime/AutoTimePreferenceController.java
@@ -27,6 +27,8 @@
import android.app.time.TimeManager;
import android.content.Context;
+import androidx.preference.Preference;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
@@ -77,6 +79,17 @@
}
@Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ refreshSummary(preference);
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return mContext.getString(R.string.date_time_auto_summary);
+ }
+
+ @Override
public boolean isChecked() {
return isEnabled();
}
diff --git a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
index 8eccf31..2f06289 100644
--- a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
+++ b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
@@ -129,11 +129,11 @@
// time zone must use location.
if (LocationProviderStatusPreferenceController.hasLocationTimeZoneNoTelephonyFallback(
mTimeManager.getTimeZoneCapabilitiesAndConfig().getDetectorStatus())) {
- return mContext.getResources().getString(R.string.auto_zone_requires_location_summary);
+ return mContext.getString(R.string.auto_zone_requires_location_summary);
}
- // If the user has a dedicated toggle to control location use, the summary can
- // be empty because the use of location is explicit.
- return "";
+
+ // If the user has a dedicated toggle to control location use, explain what it does.
+ return mContext.getString(R.string.zone_auto_title_summary);
}
@VisibleForTesting
diff --git a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
index 3cb3025..c93b450 100644
--- a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
+++ b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
@@ -15,13 +15,16 @@
import static android.Manifest.permission_group.LOCATION;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.icu.text.RelativeDateTimeFormatter;
+import android.location.LocationManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -43,6 +46,8 @@
* Preference controller that handles the display of apps that access locations.
*/
public class RecentLocationAccessPreferenceController extends LocationBasePreferenceController {
+ private static final String TAG = RecentLocationAccessPreferenceController.class
+ .getSimpleName();
public static final int MAX_APPS = 3;
@VisibleForTesting
RecentAppOpsAccess mRecentLocationApps;
@@ -51,7 +56,8 @@
private boolean mShowSystem = false;
private boolean mSystemSettingChanged = false;
- private static class PackageEntryClickedListener implements
+ @VisibleForTesting
+ static class PackageEntryClickedListener implements
Preference.OnPreferenceClickListener {
private final Context mContext;
private final String mPackage;
@@ -66,12 +72,28 @@
@Override
public boolean onPreferenceClick(Preference preference) {
- final Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
- intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
- intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, LOCATION);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackage);
- intent.putExtra(Intent.EXTRA_USER, mUserHandle);
- mContext.startActivity(intent);
+ if (mPackage.equals(mContext.getSystemService(LocationManager.class)
+ .getExtraLocationControllerPackage())) {
+ try {
+ mContext.startActivityAsUser(
+ new Intent(Settings.ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS),
+ mUserHandle);
+ } catch (ActivityNotFoundException e) {
+ // In rare cases where location controller extra package is set, but
+ // no activity exists to handle the location controller extra package settings
+ // intent, log an error instead of crashing.
+ Log.e(TAG, "No activity to handle "
+ + "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS");
+ }
+ } else {
+ final Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
+ intent.setPackage(mContext.getPackageManager()
+ .getPermissionControllerPackageName());
+ intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, LOCATION);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackage);
+ intent.putExtra(Intent.EXTRA_USER, mUserHandle);
+ mContext.startActivity(intent);
+ }
return true;
}
}
diff --git a/src/com/android/settings/network/apn/ApnEditor.java b/src/com/android/settings/network/apn/ApnEditor.java
index d1b2f2f..dc9741d 100644
--- a/src/com/android/settings/network/apn/ApnEditor.java
+++ b/src/com/android/settings/network/apn/ApnEditor.java
@@ -198,6 +198,10 @@
public static final String APN_TYPE_MCX = "mcx";
/** APN type for XCAP */
public static final String APN_TYPE_XCAP = "xcap";
+ /** APN type for OEM_PAID networks (Automotive PANS) */
+ public static final String APN_TYPE_OEM_PAID = "oem_paid";
+ /** APN type for OEM_PRIVATE networks (Automotive PANS) */
+ public static final String APN_TYPE_OEM_PRIVATE = "oem_private";
/** Array of all APN types */
public static final String[] APN_TYPES = {APN_TYPE_DEFAULT,
APN_TYPE_MMS,
@@ -211,6 +215,14 @@
APN_TYPE_EMERGENCY,
APN_TYPE_MCX,
APN_TYPE_XCAP,
+ APN_TYPE_OEM_PAID,
+ APN_TYPE_OEM_PRIVATE,
+ };
+
+ /** Array of APN types that are never user-editable */
+ private static final String[] ALWAYS_READ_ONLY_APN_TYPES = new String[] {
+ APN_TYPE_OEM_PAID,
+ APN_TYPE_OEM_PRIVATE,
};
/**
@@ -361,6 +373,18 @@
}
/**
+ * Fetch complete list of read only APN types.
+ *
+ * The list primarily comes from carrier config, but is also supplied by APN types which are
+ * always read only.
+ */
+ static String[] getReadOnlyApnTypes(PersistableBundle b) {
+ String[] carrierReadOnlyApnTypes = b.getStringArray(
+ CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
+ return ArrayUtils.concat(String.class, carrierReadOnlyApnTypes, ALWAYS_READ_ONLY_APN_TYPES);
+ }
+
+ /**
* Enable ProxySubscriptionMgr with Lifecycle support for all controllers
* live within this fragment
*/
@@ -1355,8 +1379,7 @@
if (configManager != null) {
final PersistableBundle b = configManager.getConfigForSubId(mSubId);
if (b != null) {
- mReadOnlyApnTypes = b.getStringArray(
- CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
+ mReadOnlyApnTypes = getReadOnlyApnTypes(b);
if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) {
Log.d(TAG,
"onCreate: read only APN type: " + Arrays.toString(mReadOnlyApnTypes));
diff --git a/src/com/android/settings/network/apn/ApnSettings.java b/src/com/android/settings/network/apn/ApnSettings.java
index 2debba1..0e3c3a4 100644
--- a/src/com/android/settings/network/apn/ApnSettings.java
+++ b/src/com/android/settings/network/apn/ApnSettings.java
@@ -135,8 +135,7 @@
mHideImsApn = b.getBoolean(CarrierConfigManager.KEY_HIDE_IMS_APN_BOOL);
mAllowAddingApns = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL);
if (mAllowAddingApns) {
- final String[] readOnlyApnTypes = b.getStringArray(
- CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
+ final String[] readOnlyApnTypes = ApnEditor.getReadOnlyApnTypes(b);
// if no apn type can be edited, do not allow adding APNs
if (ApnEditor.hasAllApns(readOnlyApnTypes)) {
Log.d(TAG, "not allowing adding APN because all APN types are read only");
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index 6492d39..68588bb 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -204,9 +204,7 @@
CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL
)
val customizedConfig = CustomizedConfig(
- readOnlyApnTypes = b.getStringArray(
- CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY
- )?.toList() ?: emptyList(),
+ readOnlyApnTypes = ApnEditor.getReadOnlyApnTypes(b)?.toList() ?: emptyList(),
readOnlyApnFields = b.getStringArray(
CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY
)?.toList() ?: emptyList(),
diff --git a/src/com/android/settings/network/apn/ApnTypes.kt b/src/com/android/settings/network/apn/ApnTypes.kt
index b9bc480..4f84ac7 100644
--- a/src/com/android/settings/network/apn/ApnTypes.kt
+++ b/src/com/android/settings/network/apn/ApnTypes.kt
@@ -45,6 +45,8 @@
ApnSetting.TYPE_VSIM_STRING,
ApnSetting.TYPE_BIP_STRING,
ApnSetting.TYPE_ENTERPRISE_STRING,
+ ApnSetting.TYPE_OEM_PAID_STRING,
+ ApnSetting.TYPE_OEM_PRIVATE_STRING,
)
private fun splitToList(apnType: String): List<String> {
diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
index 6b5b4cb..43bba07 100644
--- a/src/com/android/settings/network/telephony/SubscriptionRepository.kt
+++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
@@ -23,11 +23,13 @@
import androidx.lifecycle.LifecycleOwner
import com.android.settings.network.SubscriptionUtil
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -36,6 +38,8 @@
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
private const val TAG = "SubscriptionRepository"
@@ -132,20 +136,7 @@
fun canDisablePhysicalSubscription() = subscriptionManager.canDisablePhysicalSubscription()
/** Flow for subscriptions changes. */
- fun subscriptionsChangedFlow() = callbackFlow {
- val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() {
- override fun onSubscriptionsChanged() {
- trySend(Unit)
- }
- }
-
- subscriptionManager.addOnSubscriptionsChangedListener(
- Dispatchers.Default.asExecutor(),
- listener,
- )
-
- awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) }
- }.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default)
+ fun subscriptionsChangedFlow() = getSharedSubscriptionsChangedFlow(context)
/** Flow of active subscription ids. */
fun activeSubscriptionIdListFlow(): Flow<List<Int>> =
@@ -172,6 +163,57 @@
flowOf(null)
}
}
+
+ companion object {
+ private lateinit var SharedSubscriptionsChangedFlow: Flow<Unit>
+
+ private fun getSharedSubscriptionsChangedFlow(context: Context): Flow<Unit> {
+ if (!this::SharedSubscriptionsChangedFlow.isInitialized) {
+ SharedSubscriptionsChangedFlow =
+ context.applicationContext
+ .requireSubscriptionManager()
+ .subscriptionsChangedFlow()
+ .shareIn(
+ scope = CoroutineScope(Dispatchers.Default),
+ started = SharingStarted.WhileSubscribed(),
+ replay = 1,
+ )
+ }
+ return SharedSubscriptionsChangedFlow
+ }
+
+ /**
+ * Flow for subscriptions changes.
+ *
+ * Note: Even the SubscriptionManager.addOnSubscriptionsChangedListener's doc says the
+ * SubscriptionManager.OnSubscriptionsChangedListener.onSubscriptionsChanged() method will
+ * also be invoked once initially when calling it, there still case that the
+ * onSubscriptionsChanged() method is not invoked initially. For example, when the
+ * onSubscriptionsChanged event never happens before, on a device never ever has any
+ * subscriptions.
+ */
+ private fun SubscriptionManager.subscriptionsChangedFlow() =
+ callbackFlow {
+ val listener =
+ object : SubscriptionManager.OnSubscriptionsChangedListener() {
+ override fun onSubscriptionsChanged() {
+ trySend(Unit)
+ }
+
+ override fun onAddListenerFailed() {
+ close()
+ }
+ }
+
+ addOnSubscriptionsChangedListener(Dispatchers.Default.asExecutor(), listener)
+
+ awaitClose { removeOnSubscriptionsChangedListener(listener) }
+ }
+ .onStart { emit(Unit) } // Ensure this flow is never empty
+ .conflate()
+ .onEach { Log.d(TAG, "subscriptions changed") }
+ .flowOn(Dispatchers.Default)
+ }
}
val Context.subscriptionManager: SubscriptionManager?
diff --git a/src/com/android/settings/notification/LockscreenNotificationMinimalismPreferenceController.java b/src/com/android/settings/notification/LockscreenNotificationMinimalismPreferenceController.java
new file mode 100644
index 0000000..7b48ba7
--- /dev/null
+++ b/src/com/android/settings/notification/LockscreenNotificationMinimalismPreferenceController.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM;
+import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.server.notification.Flags;
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+public class LockscreenNotificationMinimalismPreferenceController
+ extends TogglePreferenceController {
+
+ @VisibleForTesting
+ static final int ON = 1;
+ @VisibleForTesting
+ static final int OFF = 0;
+
+ public LockscreenNotificationMinimalismPreferenceController(
+ Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ LOCK_SCREEN_NOTIFICATION_MINIMALISM, ON) == ON;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ return Settings.Secure.putInt(mContext.getContentResolver(),
+ LOCK_SCREEN_NOTIFICATION_MINIMALISM, isChecked ? ON : OFF);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!Flags.notificationMinimalism()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ int lockScreenNotif = Settings.Secure.getInt(mContext.getContentResolver(),
+ LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+ if (lockScreenNotif == 0) {
+ return DISABLED_DEPENDENT_SETTING;
+ }
+ return AVAILABLE;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_notifications;
+ }
+}
diff --git a/src/com/android/settings/notification/ShowOnlyUnseenNotificationsOnLockscreenPreferenceController.java b/src/com/android/settings/notification/ShowOnlyUnseenNotificationsOnLockscreenPreferenceController.java
index a37e29d..9534483 100644
--- a/src/com/android/settings/notification/ShowOnlyUnseenNotificationsOnLockscreenPreferenceController.java
+++ b/src/com/android/settings/notification/ShowOnlyUnseenNotificationsOnLockscreenPreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.notification;
+import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS;
import android.content.Context;
@@ -23,6 +24,7 @@
import androidx.annotation.VisibleForTesting;
+import com.android.server.notification.Flags;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
@@ -55,6 +57,13 @@
@Override
public int getAvailabilityStatus() {
+ if (Flags.notificationMinimalism()) {
+ if (!isNotifOnLockScreenEnabled()) {
+ return DISABLED_DEPENDENT_SETTING;
+ }
+ // We want to show the switch when the lock screen notification minimalism flag is on.
+ return AVAILABLE;
+ }
int setting = Settings.Secure.getInt(mContext.getContentResolver(),
LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, UNSET);
if (setting == UNSET) {
@@ -68,4 +77,9 @@
public int getSliceHighlightMenuRes() {
return R.string.menu_key_notifications;
}
+
+ private boolean isNotifOnLockScreenEnabled() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) == 1;
+ }
}
diff --git a/src/com/android/settings/sim/PreferredSimDialogFragment.java b/src/com/android/settings/sim/PreferredSimDialogFragment.java
index 96c0eb6..aa42f3c 100644
--- a/src/com/android/settings/sim/PreferredSimDialogFragment.java
+++ b/src/com/android/settings/sim/PreferredSimDialogFragment.java
@@ -21,10 +21,12 @@
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
+import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -91,8 +93,12 @@
}
if (dialog == null) {
- Log.d(TAG, "Dialog is null.");
- dismiss();
+ dismiss("Dialog is null.");
+ return;
+ }
+ Context context = getContext();
+ if (context == null) {
+ dismiss("getContext is null.");
return;
}
@@ -100,24 +106,44 @@
if (info == null || (info.isEmbedded()
&& (info.getProfileClass() == PROFILE_CLASS_PROVISIONING
|| (Flags.oemEnabledSatelliteFlag() && info.isOnlyNonTerrestrialNetwork())))) {
- dismiss();
+ dismiss("SubscriptionInfo is null or other esim's cases.");
return;
}
Log.d(TAG, "SubscriptionInfo: " + info);
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ if (telephonyManager == null) {
+ dismiss("TelephonyManager is null.");
+ return;
+ }
+ telephonyManager = telephonyManager.createForSubscriptionId(info.getSubscriptionId());
+ if (telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)) {
+ dismiss("mobile data is on.");
+ final SimDialogActivity activity = (SimDialogActivity) getActivity();
+ if (activity != null) {
+ activity.finish();
+ }
+ return;
+ }
+
final CharSequence simName =
- SubscriptionUtil.getUniqueSubscriptionDisplayName(info, getContext());
+ SubscriptionUtil.getUniqueSubscriptionDisplayName(info, context);
final String title =
- getContext().getString(
+ context.getString(
getTitleResId(),
simName);
final String message =
- getContext().getString(
+ context.getString(
R.string.sim_preferred_message,
simName);
dialog.setTitle(title);
dialog.setMessage(message);
}
+ private void dismiss(String log) {
+ Log.d(TAG, log);
+ dismiss();
+ }
+
@Override
public void updateDialog() {
updateDialog((AlertDialog) getDialog());
diff --git a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
index 4dc2e9e..b601eb3 100644
--- a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
+++ b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
@@ -356,10 +356,11 @@
log("setSpeedType(), setBand(BAND_2GHZ)");
configBuilder.setBand(BAND_2GHZ);
}
- // Set the security type back to WPA2/WPA3 if we're moving from 6GHz to something else.
- if ((config.getBand() & BAND_6GHZ) != 0) {
- configBuilder.setPassphrase(
- generatePassword(config), SECURITY_TYPE_WPA3_SAE_TRANSITION);
+ // Set the security type back to WPA2/WPA3 if the password is at least 8 characters and
+ // we're moving from 6GHz to something else.
+ String passphrase = generatePassword(config);
+ if ((passphrase.length() >= 8) && (config.getBand() & BAND_6GHZ) != 0) {
+ configBuilder.setPassphrase(passphrase, SECURITY_TYPE_WPA3_SAE_TRANSITION);
}
}
setSoftApConfiguration(configBuilder.build());
diff --git a/tests/robotests/src/com/android/settings/SettingsInitializeTest.java b/tests/robotests/src/com/android/settings/SettingsInitializeTest.java
index a8f42c2..467436b 100644
--- a/tests/robotests/src/com/android/settings/SettingsInitializeTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsInitializeTest.java
@@ -24,6 +24,7 @@
import android.content.pm.ShortcutManager;
+import com.android.settings.core.instrumentation.ElapsedTimeUtils;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
@@ -96,4 +97,12 @@
assertThat(updatedShortcuts).hasSize(1);
assertThat(updatedShortcuts.get(0)).isSameInstanceAs(info);
}
+
+ @Test
+ public void onReceive_suwFinished_shouldHaveElapsedTime() {
+ mSettingsInitialize.onReceive(mContext, new Intent(SetupWizardUtils.ACTION_SETUP_WIZARD_FINISHED));
+
+ final long elapsedTime = ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis());
+ assertThat(elapsedTime).isNotEqualTo(-1L);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
index 9f0cb6e..949b3d8 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
@@ -46,17 +46,21 @@
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair;
-import android.widget.TextView;
-import androidx.appcompat.app.AlertDialog;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.flags.Flags;
@@ -73,8 +77,14 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Executor;
/** Tests for {@link BluetoothDevicePairingDetailBase}. */
@@ -82,7 +92,7 @@
@Config(shadows = {
ShadowBluetoothAdapter.class,
ShadowAlertDialogCompat.class,
- com.android.settings.testutils.shadow.ShadowFragment.class,
+ ShadowFragment.class,
})
public class BluetoothDevicePairingDetailBaseTest {
@@ -133,7 +143,6 @@
mFragment.mLocalManager = mLocalManager;
mFragment.mBluetoothAdapter = mBluetoothAdapter;
mFragment.initPreferencesFromPreferenceScreen();
-
}
@Test
@@ -199,22 +208,26 @@
}
@Test
+ @Config(shadows = ShadowDialogFragment.class)
public void onDeviceBondStateChanged_bonded_pairAndJoinSharingEnabled_handle() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ ShadowDialogFragment.reset();
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
shadowOf(Looper.getMainLooper()).idle();
- AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
- assertThat(dialog).isNotNull();
- TextView message = dialog.findViewById(R.id.message);
- assertThat(message).isNotNull();
- assertThat(message.getText().toString()).isEqualTo(
+ ProgressDialogFragment progressDialog = mFragment.mProgressDialog;
+ assertThat(progressDialog).isNotNull();
+ assertThat(progressDialog.getMessage()).isEqualTo(
mContext.getString(R.string.progress_dialog_connect_device_content,
TEST_DEVICE_ADDRESS));
+ assertThat(
+ ShadowDialogFragment.isIsShowing(ProgressDialogFragment.class.getName())).isTrue();
verify(mFragment, never()).finish();
+
+ ShadowDialogFragment.reset();
}
@Test
@@ -283,9 +296,11 @@
}
@Test
+ @Config(shadows = ShadowDialogFragment.class)
public void
onProfileConnectionStateChanged_deviceInSelectedListAndConnected_pairAndJoinSharing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ ShadowDialogFragment.reset();
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
@@ -309,6 +324,8 @@
assertThat(btDevice).isNotNull();
assertThat(btDevice).isEqualTo(mBluetoothDevice);
verify(mFragment).finish();
+
+ ShadowDialogFragment.reset();
}
@Test
@@ -393,7 +410,13 @@
doReturn(intent).when(activity).getIntent();
doReturn(activity).when(mFragment).getActivity();
FragmentManager fragmentManager = mock(FragmentManager.class);
+ FragmentTransaction fragmentTransaction = mock(FragmentTransaction.class);
+ doReturn(fragmentTransaction).when(fragmentManager).beginTransaction();
doReturn(fragmentManager).when(mFragment).getFragmentManager();
+ doReturn(fragmentManager).when(mFragment).getChildFragmentManager();
+ Lifecycle lifecycle = mock(Lifecycle.class);
+ when(lifecycle.getCurrentState()).thenReturn(Lifecycle.State.RESUMED);
+ doReturn(lifecycle).when(mFragment).getLifecycle();
mFragment.mShouldTriggerAudioSharingShareThenPairFlow =
mFragment.shouldTriggerAudioSharingShareThenPairFlow();
}
@@ -425,4 +448,41 @@
return "test_tag";
}
}
+
+ /** Shadow of DialogFragment. */
+ @Implements(value = DialogFragment.class)
+ public static class ShadowDialogFragment {
+ @RealObject
+ private DialogFragment mDialogFragment;
+ private static Map<String, Boolean> sDialogStatus = new HashMap<>();
+
+ /** Resetter of the shadow. */
+ @Resetter
+ public static void reset() {
+ sDialogStatus.clear();
+ }
+
+ /** Implementation for DialogFragment#show. */
+ @Implementation
+ public void show(@NonNull FragmentManager manager, @Nullable String tag) {
+ sDialogStatus.put(mDialogFragment.getClass().getName(), true);
+ }
+
+ /** Implementation for DialogFragment#dismissAllowingStateLoss. */
+ @Implementation
+ public void dismissAllowingStateLoss() {
+ sDialogStatus.put(mDialogFragment.getClass().getName(), false);
+ }
+
+ /** Implementation for DialogFragment#dismiss. */
+ @Implementation
+ public void dismiss() {
+ sDialogStatus.put(mDialogFragment.getClass().getName(), false);
+ }
+
+ /** Check if DialogFragment is showing. */
+ public static boolean isIsShowing(String clazzName) {
+ return sDialogStatus.getOrDefault(clazzName, false);
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ProgressDialogFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/ProgressDialogFragmentTest.java
new file mode 100644
index 0000000..7468776
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/ProgressDialogFragmentTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
+
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowAlertDialogCompat.class})
+public class ProgressDialogFragmentTest {
+ @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
+ private static final String TEST_MESSAGE1 = "message1";
+ private static final String TEST_MESSAGE2 = "message2";
+
+ private Fragment mParent;
+
+ @Before
+ public void setUp() {
+ ShadowAlertDialogCompat.reset();
+ mParent = new Fragment();
+ FragmentController.setupFragment(
+ mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowAlertDialogCompat.reset();
+ }
+
+ @Test
+ public void getMetricsCategory_correctValue() {
+ ProgressDialogFragment fragment = ProgressDialogFragment.newInstance(mParent);
+ // TODO: update real metric
+ assertThat(fragment.getMetricsCategory()).isEqualTo(0);
+ }
+
+ @Test
+ public void onCreateDialog_unattachedFragment_nullDialogFragment() {
+ ProgressDialogFragment fragment = ProgressDialogFragment.newInstance(new Fragment());
+ assertThat(fragment).isNull();
+ }
+
+ @Test
+ public void onCreateDialog_showDialog() {
+ ProgressDialogFragment fragment = ProgressDialogFragment.newInstance(mParent);
+ fragment.show(TEST_MESSAGE1);
+ shadowMainLooper().idle();
+ AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+ assertThat(dialog.isShowing()).isTrue();
+ TextView view = dialog.findViewById(R.id.message);
+ assertThat(view).isNotNull();
+ assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE1);
+ }
+
+ @Test
+ public void dismissDialog_succeed() {
+ ProgressDialogFragment fragment = ProgressDialogFragment.newInstance(mParent);
+ fragment.show(TEST_MESSAGE1);
+ shadowMainLooper().idle();
+ AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+ assertThat(dialog.isShowing()).isTrue();
+
+ fragment.dismissAllowingStateLoss();
+ shadowMainLooper().idle();
+ assertThat(dialog.isShowing()).isFalse();
+ }
+
+ @Test
+ public void showDialog_sameMessage_keepExistingDialog() {
+ ProgressDialogFragment fragment = ProgressDialogFragment.newInstance(mParent);
+ fragment.show(TEST_MESSAGE1);
+ shadowMainLooper().idle();
+ AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+ assertThat(dialog.isShowing()).isTrue();
+
+ fragment.show(TEST_MESSAGE1);
+ shadowMainLooper().idle();
+ assertThat(dialog.isShowing()).isTrue();
+ TextView view = dialog.findViewById(R.id.message);
+ assertThat(view).isNotNull();
+ assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE1);
+ }
+
+ @Test
+ public void showDialog_newMessage_keepAndUpdateDialog() {
+ ProgressDialogFragment fragment = ProgressDialogFragment.newInstance(mParent);
+ fragment.show(TEST_MESSAGE1);
+ shadowMainLooper().idle();
+ AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+ assertThat(dialog.isShowing()).isTrue();
+ TextView view = dialog.findViewById(R.id.message);
+ assertThat(view).isNotNull();
+ assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE1);
+
+ fragment.show(TEST_MESSAGE2);
+ shadowMainLooper().idle();
+ assertThat(dialog.isShowing()).isTrue();
+ assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE2);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt
index 51c0c30..1ea8044 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt
+++ b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt
@@ -178,11 +178,9 @@
}.launchIn(testScope.backgroundScope)
delay(100)
runCurrent()
- helpPreference!!.onClick()
ShadowLooper.idleMainLooper()
- val shadowActivity = Shadows.shadowOf(fragmentActivity)
- assertThat(shadowActivity.nextStartedActivity).isSameInstanceAs(intent)
+ assertThat(helpPreference?.intent).isSameInstanceAs(intent)
}
}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
index abdd743..bfb474b 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
@@ -80,6 +80,7 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.android.util.concurrent.InlineExecutorService;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
@@ -143,6 +144,8 @@
mAudioStreamMediaService = spy(new AudioStreamMediaService());
ReflectionHelpers.setField(mAudioStreamMediaService, "mBase", mContext);
+ ReflectionHelpers.setField(
+ mAudioStreamMediaService, "mExecutor", new InlineExecutorService());
when(mAudioStreamMediaService.getSystemService(anyString()))
.thenReturn(mMediaSessionManager);
when(mMediaSessionManager.createSession(any(), anyString(), any())).thenReturn(mISession);
@@ -353,18 +356,6 @@
}
@Test
- public void mediaSessionCallback_onSeekTo_updateNotification() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-
- mAudioStreamMediaService.onCreate();
- mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
- assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
- mAudioStreamMediaService.mMediaSessionCallback.onSeekTo(100);
-
- verify(mNotificationManager).notify(anyInt(), any());
- }
-
- @Test
public void mediaSessionCallback_onPause_setVolume() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
@@ -416,19 +407,6 @@
}
@Test
- public void volumeControlCallback_onDeviceVolumeChanged_updateNotification() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-
- mAudioStreamMediaService.onCreate();
- assertThat(mAudioStreamMediaService.mVolumeControlCallback).isNotNull();
- mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
- mAudioStreamMediaService.mVolumeControlCallback.onDeviceVolumeChanged(
- mDevice, /* volume= */ 0);
-
- verify(mNotificationManager).notify(anyInt(), any());
- }
-
- @Test
public void onBind_returnNull() {
IBinder binder = mAudioStreamMediaService.onBind(new Intent());
diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimePreferenceControllerTest.java
index 9ac318a..ffb5141 100644
--- a/tests/robotests/src/com/android/settings/datetime/AutoTimePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/AutoTimePreferenceControllerTest.java
@@ -34,6 +34,8 @@
import androidx.preference.Preference;
+import com.android.settings.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -153,6 +155,12 @@
assertThat(mController.isEnabled()).isFalse();
}
+ @Test
+ public void getSummary() {
+ assertThat(mController.getSummary().toString()).isEqualTo(
+ mContext.getString(R.string.date_time_auto_summary));
+ }
+
private static TimeCapabilitiesAndConfig createCapabilitiesAndConfig(boolean autoSupported,
boolean autoEnabled) {
int configureAutoDetectionEnabledCapability =
diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
index c2d0445..651915b 100644
--- a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
@@ -225,7 +225,8 @@
when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
when(mTimeManager.updateTimeZoneConfiguration(Mockito.any())).thenReturn(true);
- assertThat(mController.getSummary()).isEqualTo("");
+ assertThat(mController.getSummary().toString()).isEqualTo(
+ mContext.getString(R.string.zone_auto_title_summary));
capabilitiesAndConfig = createCapabilitiesAndConfig(
/* autoSupported= */true, /* autoEnabled= */true, /* telephonySupported= */
@@ -233,7 +234,7 @@
when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
when(mTimeManager.updateTimeZoneConfiguration(Mockito.any())).thenReturn(true);
- assertThat(mController.getSummary()).isEqualTo(
+ assertThat(mController.getSummary().toString()).isEqualTo(
mContext.getString(R.string.auto_zone_requires_location_summary));
}
diff --git a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
index e9284ee..7673f38 100644
--- a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
@@ -17,12 +17,15 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.Intent;
+import android.location.LocationManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.view.LayoutInflater;
@@ -65,7 +68,8 @@
private DashboardFragment mDashboardFragment;
@Mock
private RecentAppOpsAccess mRecentLocationApps;
-
+ @Mock
+ private LocationManager mLocationManager;
private Context mContext;
private RecentLocationAccessPreferenceController mController;
private View mAppEntitiesHeaderView;
@@ -130,4 +134,23 @@
mContext.getContentResolver(), Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 1);
verify(mLayoutPreference, Mockito.times(1)).addPreference(Mockito.any());
}
+
+ @Test
+ public void testPreferenceClick_onExtraLocationPackage_startsExtraLocationActivity() {
+ String extraLocationPkgName = "extraLocationPkgName";
+ when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);
+ when(mLocationManager.getExtraLocationControllerPackage()).thenReturn(extraLocationPkgName);
+ RecentLocationAccessPreferenceController.PackageEntryClickedListener listener =
+ new RecentLocationAccessPreferenceController.PackageEntryClickedListener(
+ mContext, extraLocationPkgName, UserHandle.CURRENT);
+ doNothing().when(mContext).startActivityAsUser(Mockito.refEq(new Intent(
+ Settings.ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS)),
+ Mockito.eq(UserHandle.CURRENT));
+
+ listener.onPreferenceClick(mLayoutPreference);
+
+ verify(mContext).startActivityAsUser(Mockito.refEq(new Intent(
+ Settings.ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS)),
+ Mockito.eq(UserHandle.CURRENT));
+ }
}
diff --git a/tests/robotests/src/com/android/settings/notification/LockscreenNotificationMinimalismPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/LockscreenNotificationMinimalismPreferenceControllerTest.java
new file mode 100644
index 0000000..86dc06f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/LockscreenNotificationMinimalismPreferenceControllerTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class LockscreenNotificationMinimalismPreferenceControllerTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ private LockscreenNotificationMinimalismPreferenceController mController;
+ private Preference mPreference;
+ static final int ON = 1;
+ static final int OFF = 0;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mock(DevicePolicyManager.class)).when(mContext)
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mController = new LockscreenNotificationMinimalismPreferenceController(mContext,
+ "key");
+ mPreference = new Preference(RuntimeEnvironment.application);
+ mPreference.setKey(mController.getPreferenceKey());
+ when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_MINIMALISM)
+ public void display_featureFlagOff_shouldNotDisplay() {
+ mController.displayPreference(mScreen);
+ assertThat(mPreference.isVisible()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_MINIMALISM)
+ public void display_featureFlagOn_shouldDisplay() {
+ mController.displayPreference(mScreen);
+ assertThat(mPreference.isVisible()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_MINIMALISM)
+ public void isChecked_settingIsOff_shouldReturnFalse() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ LOCK_SCREEN_NOTIFICATION_MINIMALISM, OFF);
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_MINIMALISM)
+ public void isChecked_settingIsOn_shouldReturnTrue() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ LOCK_SCREEN_NOTIFICATION_MINIMALISM, ON);
+
+ assertThat(mController.isChecked()).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ShowOnlyUnseenNotificationsOnLockscreenPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ShowOnlyUnseenNotificationsOnLockscreenPreferenceControllerTest.java
index cc26e54..8877f30 100644
--- a/tests/robotests/src/com/android/settings/notification/ShowOnlyUnseenNotificationsOnLockscreenPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ShowOnlyUnseenNotificationsOnLockscreenPreferenceControllerTest.java
@@ -29,12 +29,16 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -53,6 +57,8 @@
private ShowOnlyUnseenNotificationsOnLockscreenPreferenceController mController;
private Preference mPreference;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() {
@@ -67,12 +73,14 @@
}
@Test
+ @DisableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_MINIMALISM)
public void display_configUnset_shouldNotDisplay() {
mController.displayPreference(mScreen);
assertThat(mPreference.isVisible()).isFalse();
}
@Test
+ @DisableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_MINIMALISM)
public void display_configSet_showDisplay() {
Settings.Secure.putInt(mContext.getContentResolver(),
LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, OFF);
@@ -81,6 +89,15 @@
}
@Test
+ @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_MINIMALISM)
+ public void display_configUnset_minimalismEnabled_shouldDisplay() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, ON);
+ mController.displayPreference(mScreen);
+ assertThat(mPreference.isVisible()).isTrue();
+ }
+
+ @Test
public void isChecked_settingIsOff_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(),
LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, OFF);
diff --git a/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt b/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt
index f61a3d3..32ca2cd 100644
--- a/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt
+++ b/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt
@@ -38,8 +38,12 @@
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.transform
+import kotlinx.coroutines.flow.update
/** Fake to be used by other classes to easily fake the FingerprintManager implementation. */
class FakeFingerprintManagerInteractor :
@@ -52,7 +56,7 @@
RenameFingerprintInteractor,
SensorInteractor {
- var enrollableFingerprints: Int = 5
+ private val enrollableFingerprints = MutableStateFlow(5)
var enrolledFingerprintsInternal: MutableList<FingerprintData> = mutableListOf()
var challengeToGenerate: Pair<Long, ByteArray> = Pair(-1L, byteArrayOf())
var authenticateAttempt = FingerprintAuthAttemptModel.Success(1)
@@ -82,13 +86,13 @@
override val enrolledFingerprints: Flow<List<FingerprintData>> = flow {
emit(enrolledFingerprintsInternal)
}
- override val canEnrollFingerprints: Flow<Boolean> = flow {
- emit(enrolledFingerprintsInternal.size < enrollableFingerprints)
+ override val canEnrollFingerprints: Flow<Boolean> = enrollableFingerprints.transform {
+ emit(enrolledFingerprintsInternal.size < it)
}
- override fun maxFingerprintsEnrollable(): Int {
- return enrollableFingerprints
- }
+ override val maxFingerprintsEnrollable: Flow<Int> = enrollableFingerprints.asStateFlow()
+
+ override fun setShouldUseSettingsMaxFingerprints(useSettings: Boolean) {}
override val sensorPropertiesInternal: Flow<FingerprintSensor?> = flow { emit(sensorProp) }
override val hasSideFps: Flow<Boolean> =
@@ -110,4 +114,7 @@
}
}
+ fun setMaxEnrollableFingerprints(fingerprints: Int) {
+ enrollableFingerprints.update { fingerprints }
+ }
}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.kt
index ca37082..f47c635 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.kt
@@ -62,10 +62,15 @@
on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false)
}
+ private val mockSubscriptionActivationRepository = mock<SubscriptionActivationRepository> {
+ on { isActivationChangeableFlow() } doReturn flowOf(true)
+ }
+
private val controller = MobileNetworkSwitchController(
context = context,
preferenceKey = TEST_KEY,
subscriptionRepository = mockSubscriptionRepository,
+ subscriptionActivationRepository = mockSubscriptionActivationRepository,
).apply { init(SUB_ID) }
@Test
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
index 5052f57..ba5142e 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
@@ -91,7 +91,23 @@
subInfoListener?.onSubscriptionsChanged()
- assertThat(listDeferred.await()).hasSize(2)
+ assertThat(listDeferred.await().size).isAtLeast(2)
+ }
+
+ @Test
+ fun subscriptionsChangedFlow_managerNotCallOnSubscriptionsChangedInitially() = runBlocking {
+ mockSubscriptionManager.stub {
+ on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer
+ {
+ subInfoListener =
+ it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener
+ // not call onSubscriptionsChanged here
+ }
+ }
+
+ val initialValue = repository.subscriptionsChangedFlow().firstWithTimeoutOrNull()
+
+ assertThat(initialValue).isSameInstanceAs(Unit)
}
@Test
diff --git a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
index 691b611..2623206 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
@@ -30,6 +30,7 @@
import android.os.CancellationSignal
import android.os.Handler
import com.android.settings.biometrics.GatekeeperPasswordProvider
+import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepository
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSettingsRepositoryImpl
@@ -61,7 +62,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.last
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -106,9 +107,14 @@
private val flow: FingerprintFlow = Default
private val maxFingerprints = 5
private val currUser = MutableStateFlow(0)
+ private lateinit var fingerprintEnrollRepo: FingerprintEnrollmentRepository
private val userRepo =
object : UserRepo {
override val currentUser: Flow<Int> = currUser
+
+ override fun updateUser(user: Int) {
+ currUser.update { user }
+ }
}
@Before
@@ -133,17 +139,18 @@
}
val settingsRepository = FingerprintSettingsRepositoryImpl(maxFingerprints)
- val fingerprintEnrollmentRepository =
+ fingerprintEnrollRepo =
FingerprintEnrollmentRepositoryImpl(
fingerprintManager,
userRepo,
settingsRepository,
backgroundDispatcher,
backgroundScope,
+ fingerprintSensorRepository,
)
enrolledFingerprintsInteractorUnderTest =
- EnrolledFingerprintsInteractorImpl(fingerprintManager, userId)
+ EnrolledFingerprintsInteractorImpl(fingerprintEnrollRepo)
generateChallengeInteractorUnderTest =
GenerateChallengeInteractorImpl(fingerprintManager, userId, gateKeeperPasswordProvider)
removeFingerprintsInteractorUnderTest =
@@ -153,7 +160,7 @@
authenticateInteractorImplUnderTest = AuthenticateInteractorImpl(fingerprintManager, userId)
canEnrollFingerprintsInteractorUnderTest =
- CanEnrollFingerprintsInteractorImpl(fingerprintEnrollmentRepository)
+ CanEnrollFingerprintsInteractorImpl(fingerprintEnrollRepo)
enrollInteractorUnderTest = EnrollFingerprintInteractorImpl(userId, fingerprintManager, flow)
}
@@ -163,9 +170,16 @@
testScope.runTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
- val emptyFingerprintList: List<Fingerprint> = emptyList()
- assertThat(enrolledFingerprintsInteractorUnderTest.enrolledFingerprints.last())
- .isEqualTo(emptyFingerprintList)
+ var list: List<FingerprintData>? = null
+ val job =
+ testScope.launch {
+ enrolledFingerprintsInteractorUnderTest.enrolledFingerprints.collect { list = it }
+ }
+
+ runCurrent()
+ job.cancelAndJoin()
+
+ assertThat(list!!.isEmpty())
}
@Test
@@ -174,10 +188,19 @@
val expected = Fingerprint("Finger 1,", 2, 3L)
val fingerprintList: List<Fingerprint> = listOf(expected)
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
+ // This causes the enrolled fingerprints to be updated
- val list = enrolledFingerprintsInteractorUnderTest.enrolledFingerprints.last()
+ var list: List<FingerprintData>? = null
+ val job =
+ testScope.launch {
+ enrolledFingerprintsInteractorUnderTest.enrolledFingerprints.collect { list = it }
+ }
+
+ runCurrent()
+ job.cancelAndJoin()
+
assertThat(list!!.size).isEqualTo(fingerprintList.size)
- val actual = list[0]
+ val actual = list!![0]
assertThat(actual.name).isEqualTo(expected.name)
assertThat(actual.fingerId).isEqualTo(expected.biometricId)
assertThat(actual.deviceId).isEqualTo(expected.deviceId)
@@ -220,11 +243,7 @@
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
var result: Boolean? = null
- val job =
- testScope.launch {
- canEnrollFingerprintsInteractorUnderTest.canEnrollFingerprints.collect { result = it }
- }
-
+ val job = testScope.launch { fingerprintEnrollRepo.canEnrollUser.collect { result = it } }
runCurrent()
job.cancelAndJoin()
diff --git a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt
index f59d1fc..a9ab589 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt
@@ -112,7 +112,7 @@
.toFingerprintSensor()
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = mutableListOf()
- fakeFingerprintManagerInteractor.enrollableFingerprints = 5
+ fakeFingerprintManagerInteractor.setMaxEnrollableFingerprints(5)
var canEnrollFingerprints: Boolean = false
val job = launch {
diff --git a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java
index 4765d18..510ce39 100644
--- a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java
+++ b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java
@@ -78,6 +78,7 @@
public class WifiHotspotRepositoryTest {
static final String WIFI_SSID = "wifi_ssid";
static final String WIFI_PASSWORD = "wifi_password";
+ static final String WIFI_PASSWORD_SHORT = "wifi";
static final int WIFI_5GHZ_BAND_PREFERRED = BAND_2GHZ_5GHZ;
static final int WIFI_6GHZ_BAND_PREFERRED = BAND_2GHZ_5GHZ_6GHZ;
@@ -477,7 +478,7 @@
@Test
public void setSpeedType_2g5ghzTo6ghz_setConfigSecurityToWpa3() {
- mockConfig(SPEED_2GHZ_5GHZ, SECURITY_TYPE_WPA3_SAE_TRANSITION);
+ mockConfig(SPEED_2GHZ_5GHZ, SECURITY_TYPE_WPA3_SAE_TRANSITION, WIFI_PASSWORD);
mRepository.setSpeedType(SPEED_6GHZ);
@@ -497,11 +498,33 @@
SparseIntArray channels = mSoftApConfigCaptor.getValue().getChannels();
assertThat(channels.get(BAND_2GHZ, CHANNEL_NOT_FOUND)).isNotEqualTo(CHANNEL_NOT_FOUND);
assertThat(channels.get(BAND_2GHZ_5GHZ, CHANNEL_NOT_FOUND)).isNotEqualTo(CHANNEL_NOT_FOUND);
+ }
+
+ @Test
+ public void setSpeedType_6ghzTo2g5ghzWith8CharPassphrase_changesSecurityToWpa3Transition() {
+ mockConfigSpeedType(SPEED_6GHZ);
+ mRepository.mIsDualBand = true;
+
+ mRepository.setSpeedType(SPEED_2GHZ_5GHZ);
+
+ verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture());
assertThat(mSoftApConfigCaptor.getValue().getSecurityType())
.isEqualTo(SECURITY_TYPE_WPA3_SAE_TRANSITION);
}
@Test
+ public void setSpeedType_6ghzTo2g5ghzWithLessThan8CharPassphrase_doesNotChangeSecurity() {
+ mockConfig(SECURITY_TYPE_WPA3_SAE, SPEED_6GHZ, WIFI_PASSWORD_SHORT);
+ mRepository.mIsDualBand = true;
+
+ mRepository.setSpeedType(SPEED_2GHZ_5GHZ);
+
+ verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture());
+ assertThat(mSoftApConfigCaptor.getValue().getSecurityType())
+ .isEqualTo(SECURITY_TYPE_WPA3_SAE);
+ }
+
+ @Test
public void setSpeedType_2ghzTo5ghz_setConfigBandTo5ghzPreferred() {
mockConfigSpeedType(SPEED_2GHZ);
@@ -784,18 +807,18 @@
}
private void mockConfigSecurityType(int securityType) {
- mockConfig(securityType, SPEED_2GHZ);
+ mockConfig(securityType, SPEED_2GHZ,
+ (securityType == SECURITY_TYPE_OPEN) ? null : WIFI_PASSWORD);
}
private void mockConfigSpeedType(int speedType) {
- mockConfig(SECURITY_TYPE_WPA3_SAE, speedType);
+ mockConfig(SECURITY_TYPE_WPA3_SAE, speedType, WIFI_PASSWORD);
}
- private void mockConfig(int securityType, int speedType) {
+ private void mockConfig(int securityType, int speedType, String passphrase) {
SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
// Security Type
doReturn(securityType).when(mSecurityType).getValue();
- String passphrase = (securityType == SECURITY_TYPE_OPEN) ? null : WIFI_PASSWORD;
configBuilder.setPassphrase(passphrase, securityType).build();
// Speed Type