Merge "Get the application context and pass into to a background executor." into main
diff --git a/res/layout/preference_circular_icons.xml b/res/layout/preference_circular_icons.xml
index 863d228..e1d7cfe 100644
--- a/res/layout/preference_circular_icons.xml
+++ b/res/layout/preference_circular_icons.xml
@@ -58,8 +58,8 @@
android:lineBreakWordStyle="phrase"
android:maxLines="10"/>
- <!-- Circular icons (32dp) will be ImageViews under this LinearLayout -->
- <LinearLayout
+ <!-- Circular icons (32dp) will be ImageViews under this container -->
+ <com.android.settings.notification.modes.CircularIconsView
android:id="@+id/circles_container"
android:importantForAccessibility="noHideDescendants"
android:orientation="horizontal"
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index f8337b3..8cbf5dc 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1554,7 +1554,7 @@
<item>@*android:drawable/ic_zen_mode_type_theater</item> <!-- Film reel -->
<item>@*android:drawable/ic_zen_mode_icon_book</item>
<!-- Wellbeing -->
- <item>@*android:drawable/ic_zen_mode_type_unknown</item> <!-- Lotus flower -->
+ <item>@*android:drawable/ic_zen_mode_icon_lotus_flower</item>
<item>@*android:drawable/ic_zen_mode_type_immersive</item>
<item>@*android:drawable/ic_zen_mode_icon_headphones</item>
<item>@*android:drawable/ic_zen_mode_icon_tv</item>
@@ -1565,10 +1565,10 @@
<item>@*android:drawable/ic_zen_mode_icon_fork_and_knife</item>
<item>@*android:drawable/ic_zen_mode_icon_shopping_cart</item>
<item>@*android:drawable/ic_zen_mode_icon_child</item>
- <item>@*android:drawable/ic_zen_mode_icon_rabbit</item>
<item>@*android:drawable/ic_zen_mode_icon_animal_paw</item>
<!-- Generic / abstract -->
- <item>@*android:drawable/ic_zen_mode_type_managed</item> <!-- Account -->
+ <item>@*android:drawable/ic_zen_mode_type_unknown</item> <!-- Star badge -->
+ <item>@*android:drawable/ic_zen_mode_type_managed</item> <!-- Two people / Supervisor -->
<item>@*android:drawable/ic_zen_mode_type_other</item> <!-- Star -->
<item>@*android:drawable/ic_zen_mode_icon_heart</item>
<item>@*android:drawable/ic_zen_mode_icon_house</item>
@@ -1616,10 +1616,10 @@
<item>Fork and knife</item>
<item>Shopping cart</item>
<item>Child</item>
- <item>Rabbit</item>
<item>Animal paw</item>
<!-- Generic / abstract -->
- <item>Supervisor</item>
+ <item>Star badge</item>
+ <item>Two people</item>
<item>Star</item>
<item>Heart</item>
<item>House</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4b30dc1..ab883ee 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8851,9 +8851,13 @@
<string name="nls_feature_reply_summary">It can reply to messages and take action on buttons in notifications, including snoozing or dismissing notifications and answering calls.</string>
<string name="nls_feature_settings_title">Change settings</string>
<string name="nls_feature_settings_summary">It can turn Do Not Disturb on or off and change related settings.</string>
+ <string name="nls_feature_modes_settings_summary">It can manage and activate Priority Modes, and change related settings.</string>
<string name="notification_listener_disable_warning_summary">
If you turn off notification access for <xliff:g id="notification_listener_name">%1$s</xliff:g>, Do Not Disturb access may also be turned off.
</string>
+ <string name="notification_listener_disable_modes_warning_summary">
+ If you turn off notification access for <xliff:g id="notification_listener_name">%1$s</xliff:g>, Priority Modes access may also be turned off.
+ </string>
<string name="notification_listener_disable_warning_confirm">Turn off</string>
<string name="notification_listener_disable_warning_cancel">Cancel</string>
<string name="notif_type_ongoing">Real-time</string>
@@ -9030,6 +9034,15 @@
<!-- Sound & notification > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
<string name="zen_access_empty_text">No installed apps have requested Do Not Disturb access</string>
+ <!-- Special App Access: Title for managing Priority Modes access option. [CHAR LIMIT=40] -->
+ <string name="manage_zen_modes_access_title">Priority Modes access</string>
+
+ <!-- Button title that grants 'Priority Modes' permission to an app [CHAR_LIMIT=60]-->
+ <string name="zen_modes_access_detail_switch">Allow Priority Modes access</string>
+
+ <!-- Special App Access > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
+ <string name="zen_modes_access_empty_text">No installed apps have requested Priority Modes access</string>
+
<!-- [CHAR LIMIT=NONE] Text appearing when app notifications are off -->
<string name="app_notifications_off_desc">You haven\'t allowed notifications from this app</string>
@@ -9535,6 +9548,23 @@
<!-- Priority Modes: Blurb for modes that are not of a specific type (OTHER, UNKNOWN). [CHAR LIMIT=NONE] -->
<string name="zen_mode_blurb_generic">Minimize interruptions by only allowing important people and apps to reach you</string>
+ <!-- Priority Modes: Inspirational text for a mode that activates during a fixed time schedule (e.g. 9:00-17:00). [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_inspiration_schedule_time">Set a mode that follows a regular schedule</string>
+ <!-- Priority Modes: Inspirational text for a mode that activates during calendar events. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_inspiration_schedule_calendar">Keep your device in sync with your day’s events</string>
+ <!-- Priority Modes: Inspirational text for a mode of type BEDTIME. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_inspiration_bedtime">Wake up feeling like 100%</string>
+ <!-- Priority Modes: Inspirational text for a mode of type DRIVING. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_inspiration_driving">Put safety first while on the road</string>
+ <!-- Priority Modes: Inspirational text for a mode of type IMMERSIVE. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_inspiration_immersive">Gain focus to get in the zone</string>
+ <!-- Priority Modes: Inspirational text for a mode of type THEATER. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_inspiration_theater">For moments when courtesy counts</string>
+ <!-- Priority Modes: Inspirational text for a mode of type MANAGED. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_inspiration_managed">Guided usage to help you stay in good hands</string>
+ <!-- Priority Modes: Inspirational text for modes that are not of a specific type (OTHER, UNKNOWN). [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_inspiration_generic">Take control of your attention</string>
+
<!-- Content description for help icon button [CHAR LIMIT=20] -->
<string name="warning_button_text">Warning</string>
@@ -10134,6 +10164,18 @@
<!-- Zen mode access settings - summary for warning dialog when revoking access [CHAR LIMIT=NONE] -->
<string name="zen_access_revoke_warning_dialog_summary">All Do Not Disturb rules created by this app will be removed.</string>
+ <!-- Priority modes access settings - title for warning dialog when enabling access [CHAR LIMIT=NONE] -->
+ <string name="zen_modes_access_warning_dialog_title">Allow access to Priority Modes for <xliff:g id="app" example="Tasker">%1$s</xliff:g>?</string>
+
+ <!-- Priority modes access settings - summary for warning dialog when enabling access [CHAR LIMIT=NONE] -->
+ <string name="zen_modes_access_warning_dialog_summary">The app will be able to turn on/off Do Not Disturb, manage and activate Priority Modes, and make changes to related settings.</string>
+
+ <!-- Priority modes access settings - title for warning dialog when revoking access [CHAR LIMIT=NONE] -->
+ <string name="zen_modes_access_revoke_warning_dialog_title">Revoke access Priority Modes for <xliff:g id="app" example="Tasker">%1$s</xliff:g>?</string>
+
+ <!-- Priority modes access settings - summary for warning dialog when revoking access [CHAR LIMIT=NONE] -->
+ <string name="zen_modes_access_revoke_warning_dialog_summary">All modes created by this app will be removed.</string>
+
<!-- Ignore battery optimizations on label [CHAR LIMIT=30] -->
<string name="ignore_optimizations_on">Don\u2019t optimize</string>
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index eb80ac8..825d72c 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -91,9 +91,11 @@
settings:searchable="false"
settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
+ <!-- Settings search is handled by DataUsageSearchItem. -->
<Preference
android:key="data_usage_summary"
android:title="@string/app_cellular_data_usage"
+ settings:searchable="false"
settings:controller="com.android.settings.network.telephony.DataUsagePreferenceController"/>
<com.android.settings.datausage.BillingCyclePreference
diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java
index 5b052f2..d208fdf 100644
--- a/src/com/android/settings/SettingsApplication.java
+++ b/src/com/android/settings/SettingsApplication.java
@@ -18,7 +18,9 @@
import android.app.Application;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
+import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri;
import android.provider.Settings;
import android.util.FeatureFlagUtils;
@@ -74,9 +76,6 @@
// Set Spa environment.
setSpaEnvironment();
- if (Flags.fingerprintV2Enrollment()) {
- mBiometricsEnvironment = new BiometricsEnvironment(this);
- }
if (ActivityEmbeddingUtils.isSettingsSplitEnabled(this)
&& FeatureFlagUtils.isEnabled(this,
@@ -120,7 +119,20 @@
@Nullable
public BiometricsEnvironment getBiometricEnvironment() {
- return mBiometricsEnvironment;
+ if (Flags.fingerprintV2Enrollment()) {
+ if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ final FingerprintManager fpm = getSystemService(FingerprintManager.class);
+ if (mBiometricsEnvironment == null) {
+ mBiometricsEnvironment = new BiometricsEnvironment(this, fpm);
+ }
+ return mBiometricsEnvironment;
+
+ } else {
+ return null;
+ }
+
+ }
+ return null;
}
@Override
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java
index 3577946..c92f734 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java
@@ -16,6 +16,7 @@
package com.android.settings.applications.specialaccess.notificationaccess;
import android.app.Dialog;
+import android.app.Flags;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.DialogInterface;
@@ -55,7 +56,10 @@
NotificationAccessDetails parent = (NotificationAccessDetails) getTargetFragment();
final String summary = getResources().getString(
- R.string.notification_listener_disable_warning_summary, label);
+ Flags.modesApi() && Flags.modesUi()
+ ? R.string.notification_listener_disable_modes_warning_summary
+ : R.string.notification_listener_disable_warning_summary,
+ label);
return new AlertDialog.Builder(getContext())
.setMessage(summary)
.setCancelable(true)
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java
index 747a125..53181fd 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java
@@ -16,6 +16,7 @@
package com.android.settings.applications.specialaccess.notificationaccess;
import android.app.Dialog;
+import android.app.Flags;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
@@ -96,6 +97,11 @@
R.string.nls_warning_prompt, label);
((TextView) content.findViewById(R.id.prompt)).setText(prompt);
+ ((TextView) content.findViewById(R.id.settings_description)).setText(
+ Flags.modesApi() && Flags.modesUi()
+ ? R.string.nls_feature_modes_settings_summary
+ : R.string.nls_feature_settings_summary);
+
Button allowButton = content.findViewById(R.id.allow_button);
allowButton.setOnClickListener((view) -> {
parent.enable(cn);
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
index 5da2990..38317ed 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
@@ -17,6 +17,7 @@
package com.android.settings.applications.specialaccess.zenaccess;
import android.app.Dialog;
+import android.app.Flags;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.text.TextUtils;
@@ -58,9 +59,14 @@
final String label = args.getString(KEY_LABEL);
final String title = getResources().getString(
- R.string.zen_access_revoke_warning_dialog_title, label);
+ Flags.modesApi() && Flags.modesUi()
+ ? R.string.zen_modes_access_revoke_warning_dialog_title
+ : R.string.zen_access_revoke_warning_dialog_title,
+ label);
final String summary = getResources()
- .getString(R.string.zen_access_revoke_warning_dialog_summary);
+ .getString(Flags.modesApi() && Flags.modesUi()
+ ? R.string.zen_modes_access_revoke_warning_dialog_summary
+ : R.string.zen_access_revoke_warning_dialog_summary);
ZenAccessDetails parent = (ZenAccessDetails) getTargetFragment();
return new AlertDialog.Builder(getContext())
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
index e4ef48b..b489602 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
@@ -17,6 +17,7 @@
package com.android.settings.applications.specialaccess.zenaccess;
import android.app.Dialog;
+import android.app.Flags;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.text.TextUtils;
@@ -55,10 +56,15 @@
final String pkg = args.getString(KEY_PKG);
final String label = args.getString(KEY_LABEL);
- final String title = getResources().getString(R.string.zen_access_warning_dialog_title,
+ final String title = getResources().getString(
+ Flags.modesApi() && Flags.modesUi()
+ ? R.string.zen_modes_access_warning_dialog_title
+ : R.string.zen_access_warning_dialog_title,
label);
final String summary = getResources()
- .getString(R.string.zen_access_warning_dialog_summary);
+ .getString(Flags.modesApi() && Flags.modesUi()
+ ? R.string.zen_modes_access_warning_dialog_summary
+ : R.string.zen_access_warning_dialog_summary);
ZenAccessDetails parent = (ZenAccessDetails) getTargetFragment();
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
index 6f4137c..cfeeb0d 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.Flags;
import android.app.NotificationManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -28,7 +29,10 @@
import android.util.Log;
import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
@@ -48,6 +52,16 @@
return AVAILABLE;
}
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ Preference preference = screen.findPreference(getPreferenceKey());
+ if (preference != null) {
+ preference.setTitle(Flags.modesApi() && Flags.modesUi()
+ ? R.string.manage_zen_modes_access_title
+ : R.string.manage_zen_access_title);
+ }
+ }
+
public static Set<String> getPackagesRequestingNotificationPolicyAccess() {
final String[] PERM = {
android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
index ffe13e6..74903c0 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
@@ -16,9 +16,11 @@
package com.android.settings.applications.specialaccess.zenaccess;
+import android.app.Flags;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
+import android.os.UserManager;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.TwoStatePreference;
@@ -42,6 +44,9 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.zen_access_permission_details);
+ requireActivity().setTitle(Flags.modesApi() && Flags.modesUi()
+ ? R.string.manage_zen_modes_access_title
+ : R.string.manage_zen_access_title);
getSettingsLifecycle().addObserver(
new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
}
@@ -49,6 +54,11 @@
@Override
protected boolean refreshUi() {
final Context context = getContext();
+ // don't show for managed profiles
+ if (UserManager.get(context).isManagedProfile(context.getUserId())
+ && !ZenAccessController.hasAccess(context, mPackageName)) {
+ finish();
+ }
// If this app didn't declare this permission in their manifest, don't bother showing UI.
final Set<String> needAccessApps =
ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
@@ -74,6 +84,9 @@
preference.setSummary(getString(R.string.zen_access_disabled_package_warning));
return;
}
+ preference.setTitle(Flags.modesApi() && Flags.modesUi()
+ ? R.string.zen_modes_access_detail_switch
+ : R.string.zen_access_detail_switch);
preference.setChecked(ZenAccessController.hasAccess(context, mPackageName));
preference.setOnPreferenceChangeListener((p, newValue) -> {
final boolean access = (Boolean) newValue;
diff --git a/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt b/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
index 9bc920a..e3233ed 100644
--- a/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt
@@ -16,12 +16,9 @@
package com.android.settings.biometrics.fingerprint2
-import android.content.pm.PackageManager
import android.hardware.fingerprint.FingerprintManager
-import android.os.ServiceManager.ServiceNotFoundException
import android.view.MotionEvent
import android.view.accessibility.AccessibilityManager
-import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.android.internal.widget.LockPatternUtils
@@ -29,33 +26,47 @@
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepository
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl
+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.FingerprintSensorRepositoryImpl
+import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSettingsRepositoryImpl
+import com.android.settings.biometrics.fingerprint2.data.repository.UserRepoImpl
import com.android.settings.biometrics.fingerprint2.debug.data.repository.UdfpsEnrollDebugRepositoryImpl
import com.android.settings.biometrics.fingerprint2.debug.domain.interactor.DebugTouchEventInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.AuthenticateInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.CanEnrollFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollFingerprintInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl
-import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractor
-import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
-import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrolledFingerprintsInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.GenerateChallengeInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.RemoveFingerprintsInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.RenameFingerprintsInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.SensorInteractorImpl
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.VibrationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
+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.model.Settings
import java.util.concurrent.Executors
import kotlinx.coroutines.MainScope
@@ -70,43 +81,53 @@
* This code is instantiated within the [SettingsApplication], all repos should be private &
* immutable and all interactors should public and immutable
*/
-class BiometricsEnvironment(context: SettingsApplication) : ViewModelStoreOwner {
-
+class BiometricsEnvironment(
+ val context: SettingsApplication,
+ private val fingerprintManager: FingerprintManager,
+) : ViewModelStoreOwner {
private val executorService = Executors.newSingleThreadExecutor()
private val backgroundDispatcher = executorService.asCoroutineDispatcher()
private val applicationScope = MainScope()
private val gateKeeperPasswordProvider = GatekeeperPasswordProvider(LockPatternUtils(context))
- private val fingerprintManager = try {
- if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- context.getSystemService(FragmentActivity.FINGERPRINT_SERVICE) as FingerprintManager?
- } else {
- null
- }
- } catch (exception: ServiceNotFoundException){
- null
- }
+ private val userRepo = UserRepoImpl(context.userId)
+ private val fingerprintSettingsRepository =
+ FingerprintSettingsRepositoryImpl(
+ context.resources.getInteger(
+ 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 debuggingRepository: DebuggingRepository = DebuggingRepositoryImpl()
private val udfpsDebugRepo = UdfpsEnrollDebugRepositoryImpl()
- /** For now, interactors are public to those with access to the [BiometricsEnvironment] class */
- val fingerprintEnrollInteractor: FingerprintEnrollInteractor by lazy {
- FingerprintEnrollInteractorImpl(context, fingerprintManager, Settings)
- }
+ fun createSensorPropertiesInteractor(): SensorInteractor =
+ SensorInteractorImpl(fingerprintSensorRepository)
- /** [FingerprintManagerInteractor] to be used to construct view models */
- val fingerprintManagerInteractor: FingerprintManagerInteractor by lazy {
- FingerprintManagerInteractorImpl(
- context,
- backgroundDispatcher,
- fingerprintManager,
- fingerprintSensorRepository,
- gateKeeperPasswordProvider,
- fingerprintEnrollInteractor,
- )
- }
+ fun createCanEnrollFingerprintsInteractor(): CanEnrollFingerprintsInteractor =
+ CanEnrollFingerprintsInteractorImpl(fingerprintEnrollmentRepository)
+
+ fun createGenerateChallengeInteractor(): GenerateChallengeInteractor =
+ GenerateChallengeInteractorImpl(fingerprintManager, context.userId, gateKeeperPasswordProvider)
+
+ fun createFingerprintEnrollInteractor(): EnrollFingerprintInteractor =
+ EnrollFingerprintInteractorImpl(context.userId, fingerprintManager, Settings)
+
+ fun createFingerprintsEnrolledInteractor(): EnrolledFingerprintsInteractorImpl =
+ EnrolledFingerprintsInteractorImpl(fingerprintManager, context.userId)
+
+ fun createAuthenticateInteractor(): AuthenitcateInteractor =
+ AuthenticateInteractorImpl(fingerprintManager, context.userId)
+
+ fun createRemoveFingerprintInteractor(): RemoveFingerprintInteractor =
+ RemoveFingerprintsInteractorImpl(fingerprintManager, context.userId)
+
+ fun createRenameFingerprintInteractor(): RenameFingerprintInteractor =
+ RenameFingerprintsInteractorImpl(fingerprintManager, context.userId, backgroundDispatcher)
val accessibilityInteractor: AccessibilityInteractor by lazy {
AccessibilityInteractorImpl(
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt
new file mode 100644
index 0000000..22904e9
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintEnrollmentRepo.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.data.repository
+
+import android.hardware.biometrics.BiometricStateListener
+import android.hardware.fingerprint.FingerprintManager
+import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.callbackFlow
+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.withContext
+
+/** Repository that contains information about fingerprint enrollments. */
+interface FingerprintEnrollmentRepository {
+ /** The current enrollments of the user */
+ val currentEnrollments: Flow<List<FingerprintData>?>
+
+ /** Indicates if a user can enroll another fingerprint */
+ val canEnrollUser: Flow<Boolean>
+
+ fun maxFingerprintsEnrollable(): Int
+}
+
+class FingerprintEnrollmentRepositoryImpl(
+ fingerprintManager: FingerprintManager,
+ userRepo: UserRepo,
+ private val settingsRepository: FingerprintSettingsRepository,
+ backgroundDispatcher: CoroutineDispatcher,
+ applicationScope: CoroutineScope,
+) : FingerprintEnrollmentRepository {
+
+ private val enrollmentChangedFlow: Flow<Int?> =
+ callbackFlow {
+ val callback =
+ object : BiometricStateListener() {
+ override fun onEnrollmentsChanged(userId: Int, sensorId: Int, hasEnrollments: Boolean) {
+ trySend(userId)
+ }
+ }
+ withContext(backgroundDispatcher) {
+ fingerprintManager.registerBiometricStateListener(callback)
+ }
+ awaitClose {
+ // no way to unregister
+ }
+ }
+ .stateIn(applicationScope, started = SharingStarted.Eagerly, initialValue = null)
+
+ 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
+ }
+ }
+ }
+ .filterNotNull()
+ .flowOn(backgroundDispatcher)
+
+ override val canEnrollUser: Flow<Boolean> =
+ currentEnrollments.map {
+ it?.size?.let { it < settingsRepository.maxEnrollableFingerprints() } ?: false
+ }
+
+ override fun maxFingerprintsEnrollable(): Int {
+ return settingsRepository.maxEnrollableFingerprints()
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt
index 516549e..1cca532 100644
--- a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt
@@ -31,6 +31,8 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.withContext
@@ -43,10 +45,13 @@
interface FingerprintSensorRepository {
/** Get the [FingerprintSensor] */
val fingerprintSensor: Flow<FingerprintSensor>
+
+ /** Indicates if this device supports the side fingerprint sensor */
+ val hasSideFps: Flow<Boolean>
}
class FingerprintSensorRepositoryImpl(
- fingerprintManager: FingerprintManager?,
+ private val fingerprintManager: FingerprintManager,
backgroundDispatcher: CoroutineDispatcher,
activityScope: CoroutineScope,
) : FingerprintSensorRepository {
@@ -66,7 +71,7 @@
}
}
withContext(backgroundDispatcher) {
- fingerprintManager?.addAuthenticatorsRegisteredCallback(callback)
+ fingerprintManager.addAuthenticatorsRegisteredCallback(callback)
}
awaitClose {}
}
@@ -75,6 +80,9 @@
override val fingerprintSensor: Flow<FingerprintSensor> =
fingerprintPropsInternal.transform { emit(it.toFingerprintSensor()) }
+ override val hasSideFps: Flow<Boolean> =
+ fingerprintSensor.flatMapLatest { flow { emit(fingerprintManager.isPowerbuttonFps()) } }
+
companion object {
private val DEFAULT_PROPS =
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSettingsRepository.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSettingsRepository.kt
new file mode 100644
index 0000000..fe6676c
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSettingsRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.data.repository
+
+/**
+ * Repository for storing metadata about fingerprint enrollments.
+ */
+interface FingerprintSettingsRepository {
+ /**
+ * Indicates the maximum number of fingerprints enrollable
+ */
+ fun maxEnrollableFingerprints(): Int
+}
+
+class FingerprintSettingsRepositoryImpl(private val maxFingerprintsEnrollable: Int) :
+ FingerprintSettingsRepository {
+ override fun maxEnrollableFingerprints() = maxFingerprintsEnrollable
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/SimulatedTouchEventsRepository.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/SimulatedTouchEventsRepository.kt
index 3c355e7..9b7f280 100644
--- a/src/com/android/settings/biometrics/fingerprint2/data/repository/SimulatedTouchEventsRepository.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/SimulatedTouchEventsRepository.kt
@@ -16,7 +16,6 @@
package com.android.settings.biometrics.fingerprint2.data.repository
-import android.graphics.Point
import android.view.MotionEvent
import kotlinx.coroutines.flow.Flow
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/UserRepo.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/UserRepo.kt
new file mode 100644
index 0000000..720e778
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/UserRepo.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/**
+ * A repository responsible for indicating the current user.
+ */
+interface UserRepo {
+ /**
+ * This flow indicates the current user.
+ */
+ val currentUser: Flow<Int>
+}
+
+class UserRepoImpl(val currUser: Int): UserRepo {
+ override val currentUser: Flow<Int> = flowOf(currUser)
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/debug/data/repository/UdfpsEnrollDebugRepository.kt b/src/com/android/settings/biometrics/fingerprint2/debug/data/repository/UdfpsEnrollDebugRepository.kt
index 0c3152a..bc48f07 100644
--- a/src/com/android/settings/biometrics/fingerprint2/debug/data/repository/UdfpsEnrollDebugRepository.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/debug/data/repository/UdfpsEnrollDebugRepository.kt
@@ -97,6 +97,8 @@
}
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensorProps)
+ override val hasSideFps: Flow<Boolean>
+ get() = flowOf(false)
private fun pointToLeftOfSensor(sensorLocation: Rect): MotionEvent =
MotionEvent.obtain(
diff --git a/src/com/android/settings/biometrics/fingerprint2/debug/domain/interactor/DebugTouchEventInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/debug/domain/interactor/DebugTouchEventInteractorImpl.kt
index fff6b66..f6627e1 100644
--- a/src/com/android/settings/biometrics/fingerprint2/debug/domain/interactor/DebugTouchEventInteractorImpl.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/debug/domain/interactor/DebugTouchEventInteractorImpl.kt
@@ -26,4 +26,4 @@
) : TouchEventInteractor {
override val touchEvent: Flow<MotionEvent> =
udfpsSimulatedTouchEventsRepository.touchExplorationDebug
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AuthenticateInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AuthenticateInteractorImpl.kt
new file mode 100644
index 0000000..df93092
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AuthenticateInteractorImpl.kt
@@ -0,0 +1,71 @@
+/*
+ * 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 android.hardware.fingerprint.FingerprintManager
+import android.os.CancellationSignal
+import android.util.Log
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
+import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
+import kotlin.coroutines.resume
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+class AuthenticateInteractorImpl(
+ private val fingerprintManager: FingerprintManager,
+ private val userId: Int,
+) : AuthenitcateInteractor {
+
+ override suspend fun authenticate(): FingerprintAuthAttemptModel =
+ suspendCancellableCoroutine { c: CancellableContinuation<FingerprintAuthAttemptModel> ->
+ val authenticationCallback =
+ object : FingerprintManager.AuthenticationCallback() {
+
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
+ super.onAuthenticationError(errorCode, errString)
+ if (c.isCompleted) {
+ Log.d(TAG, "framework sent down onAuthError after finish")
+ return
+ }
+ c.resume(FingerprintAuthAttemptModel.Error(errorCode, errString.toString()))
+ }
+
+ override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
+ super.onAuthenticationSucceeded(result)
+ if (c.isCompleted) {
+ Log.d(TAG, "framework sent down onAuthError after finish")
+ return
+ }
+ c.resume(FingerprintAuthAttemptModel.Success(result.fingerprint?.biometricId ?: -1))
+ }
+ }
+
+ val cancellationSignal = CancellationSignal()
+ c.invokeOnCancellation { cancellationSignal.cancel() }
+ fingerprintManager.authenticate(
+ null,
+ cancellationSignal,
+ authenticationCallback,
+ null,
+ userId,
+ )
+ }
+
+ companion object {
+ private const val TAG = "AuthenticateInteractor"
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/CanEnrollFingerprintsInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/CanEnrollFingerprintsInteractorImpl.kt
new file mode 100644
index 0000000..caeea4e
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/CanEnrollFingerprintsInteractorImpl.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.domain.interactor
+
+import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepository
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
+import kotlinx.coroutines.flow.Flow
+
+class CanEnrollFingerprintsInteractorImpl(
+ 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()
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollFingerprintInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollFingerprintInteractorImpl.kt
new file mode 100644
index 0000000..3e14a64
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollFingerprintInteractorImpl.kt
@@ -0,0 +1,146 @@
+/*
+ * 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 android.hardware.fingerprint.FingerprintEnrollOptions
+import android.hardware.fingerprint.FingerprintManager
+import android.os.CancellationSignal
+import android.util.Log
+import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
+import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
+import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
+import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
+import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
+import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.onFailure
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.update
+
+class EnrollFingerprintInteractorImpl(
+ private val userId: Int,
+ private val fingerprintManager: FingerprintManager,
+ private val fingerprintFlow: FingerprintFlow,
+) : EnrollFingerprintInteractor {
+ private val enrollRequestOutstanding = MutableStateFlow(false)
+
+ override suspend fun enroll(
+ hardwareAuthToken: ByteArray?,
+ enrollReason: EnrollReason,
+ fingerprintEnrollOptions: FingerprintEnrollOptions,
+ ): Flow<FingerEnrollState> = callbackFlow {
+ // TODO (b/308456120) Improve this logic
+ if (enrollRequestOutstanding.value) {
+ Log.d(TAG, "Outstanding enroll request, waiting 150ms")
+ delay(150)
+ if (enrollRequestOutstanding.value) {
+ Log.e(TAG, "Request still present, continuing")
+ }
+ }
+
+ enrollRequestOutstanding.update { true }
+
+ var streamEnded = false
+ var totalSteps: Int? = null
+ val enrollmentCallback =
+ object : FingerprintManager.EnrollmentCallback() {
+ override fun onEnrollmentProgress(remaining: Int) {
+ // This is sort of an implementation detail, but unfortunately the API isn't
+ // very expressive. If anything we should look at changing the FingerprintManager API.
+ if (totalSteps == null) {
+ totalSteps = remaining + 1
+ }
+
+ trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure { error ->
+ Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error")
+ }
+
+ if (remaining == 0) {
+ streamEnded = true
+ enrollRequestOutstanding.update { false }
+ }
+ }
+
+ override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) {
+ trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString())).onFailure { error
+ ->
+ Log.d(TAG, "onEnrollmentHelp failed to send, due to $error")
+ }
+ }
+
+ override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) {
+ trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard)).onFailure { error ->
+ Log.d(TAG, "onEnrollmentError failed to send, due to $error")
+ }
+ Log.d(TAG, "onEnrollmentError($errMsgId)")
+ streamEnded = true
+ enrollRequestOutstanding.update { false }
+ }
+
+ override fun onUdfpsPointerDown(sensorId: Int) {
+ trySend(FingerEnrollState.PointerDown(sensorId)).onFailure { error ->
+ Log.d(TAG, "onUdfpsPointerDown failed to send, due to $error")
+ }
+ }
+
+ override fun onUdfpsPointerUp(sensorId: Int) {
+ trySend(FingerEnrollState.PointerUp(sensorId)).onFailure { error ->
+ Log.d(TAG, "onUdfpsPointerUp failed to send, due to $error")
+ }
+ }
+
+ override fun onUdfpsOverlayShown() {
+ trySend(FingerEnrollState.OverlayShown).onFailure { error ->
+ Log.d(TAG, "OverlayShown failed to send, due to $error")
+ }
+ }
+
+ override fun onAcquired(isAcquiredGood: Boolean) {
+ trySend(FingerEnrollState.Acquired(isAcquiredGood)).onFailure { error ->
+ Log.d(TAG, "Acquired failed to send, due to $error")
+ }
+ }
+ }
+
+ val cancellationSignal = CancellationSignal()
+
+ fingerprintManager.enroll(
+ hardwareAuthToken,
+ cancellationSignal,
+ userId,
+ enrollmentCallback,
+ enrollReason.toOriginalReason(),
+ fingerprintEnrollOptions,
+ )
+ awaitClose {
+ // If the stream has not been ended, and the user has stopped collecting the flow
+ // before it was over, send cancel.
+ if (!streamEnded) {
+ Log.e(TAG, "Cancel is sent from settings for enroll()")
+ cancellationSignal.cancel()
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "FingerprintEnrollStateRepository"
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrolledFingerprintsInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrolledFingerprintsInteractorImpl.kt
new file mode 100644
index 0000000..83b532e
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrolledFingerprintsInteractorImpl.kt
@@ -0,0 +1,37 @@
+/*
+ * 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 android.hardware.fingerprint.FingerprintManager
+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,
+) : EnrolledFingerprintsInteractor {
+ override val enrolledFingerprints: Flow<List<FingerprintData>?> = flow {
+ emit(
+ fingerprintManager
+ .getEnrolledFingerprints(userId)
+ ?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }
+ ?.toList()
+ )
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt
index a36832d..56a1257 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt
@@ -16,7 +16,6 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor
-import android.content.Context
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintManager
import android.os.CancellationSignal
@@ -49,7 +48,7 @@
}
class FingerprintEnrollInteractorImpl(
- private val applicationContext: Context,
+ private val userId: Int,
private val fingerprintManager: FingerprintManager?,
private val fingerprintFlow: FingerprintFlow,
) : FingerprintEnrollInteractor {
@@ -138,7 +137,7 @@
fingerprintManager?.enroll(
hardwareAuthToken,
cancellationSignal,
- applicationContext.userId,
+ userId,
enrollmentCallback,
enrollReason.toOriginalReason(),
fingerprintEnrollOptions,
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
deleted file mode 100644
index f03c94e..0000000
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.biometrics.fingerprint2.domain.interactor
-
-import android.content.Context
-import android.content.Intent
-import android.hardware.fingerprint.FingerprintEnrollOptions
-import android.hardware.fingerprint.FingerprintManager
-import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
-import android.hardware.fingerprint.FingerprintManager.RemovalCallback
-import android.os.CancellationSignal
-import android.util.Log
-import com.android.settings.biometrics.GatekeeperPasswordProvider
-import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
-import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
-import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
-import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
-import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
-import com.android.settings.password.ChooseLockSettingsHelper
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
-import kotlinx.coroutines.CancellableContinuation
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.suspendCancellableCoroutine
-import kotlinx.coroutines.withContext
-
-private const val TAG = "FingerprintManagerInteractor"
-
-class FingerprintManagerInteractorImpl(
- applicationContext: Context,
- private val backgroundDispatcher: CoroutineDispatcher,
- private val fingerprintManager: FingerprintManager?,
- fingerprintSensorRepository: FingerprintSensorRepository,
- private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
- private val fingerprintEnrollStateRepository: FingerprintEnrollInteractor,
-) : FingerprintManagerInteractor {
-
- private val maxFingerprints =
- applicationContext.resources.getInteger(
- com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser
- )
- private val applicationContext = applicationContext.applicationContext
-
- override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
- suspendCoroutine {
- val callback = GenerateChallengeCallback { _, userId, challenge ->
- val intent = Intent()
- intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gateKeeperPasswordHandle)
- val challengeToken =
- gatekeeperPasswordProvider.requestGatekeeperHat(intent, challenge, userId)
-
- gatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, false)
- val p = Pair(challenge, challengeToken)
- it.resume(p)
- }
- fingerprintManager?.generateChallenge(applicationContext.userId, callback)
- }
-
- override val enrolledFingerprints: Flow<List<FingerprintData>?> = flow {
- emit(
- fingerprintManager?.getEnrolledFingerprints(applicationContext.userId)
- ?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }?.toList()
- )
- }
-
- override val canEnrollFingerprints: Flow<Boolean> = flow {
- emit(
- fingerprintManager?.getEnrolledFingerprints(applicationContext.userId)?.size ?: maxFingerprints < maxFingerprints
- )
- }
-
- override val sensorPropertiesInternal = fingerprintSensorRepository.fingerprintSensor
-
- override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
-
- override suspend fun enroll(
- hardwareAuthToken: ByteArray?,
- enrollReason: EnrollReason,
- fingerprintEnrollOptions: FingerprintEnrollOptions,
- ): Flow<FingerEnrollState> =
- fingerprintEnrollStateRepository.enroll(
- hardwareAuthToken,
- enrollReason,
- fingerprintEnrollOptions,
- )
-
- override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
- val callback =
- object : RemovalCallback() {
- override fun onRemovalError(
- fp: android.hardware.fingerprint.Fingerprint,
- errMsgId: Int,
- errString: CharSequence,
- ) {
- it.resume(false)
- }
-
- override fun onRemovalSucceeded(
- fp: android.hardware.fingerprint.Fingerprint?,
- remaining: Int,
- ) {
- it.resume(true)
- }
- }
- fingerprintManager?.remove(
- android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId),
- applicationContext.userId,
- callback,
- )
- }
-
- override suspend fun renameFingerprint(fp: FingerprintData, newName: String) {
- withContext(backgroundDispatcher) {
- fingerprintManager?.rename(fp.fingerId, applicationContext.userId, newName)
- }
- }
-
- override suspend fun hasSideFps(): Boolean? = suspendCancellableCoroutine {
- it.resume(fingerprintManager?.isPowerbuttonFps)
- }
-
- override suspend fun authenticate(): FingerprintAuthAttemptModel =
- suspendCancellableCoroutine { c: CancellableContinuation<FingerprintAuthAttemptModel> ->
- val authenticationCallback =
- object : FingerprintManager.AuthenticationCallback() {
-
- override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
- super.onAuthenticationError(errorCode, errString)
- if (c.isCompleted) {
- Log.d(TAG, "framework sent down onAuthError after finish")
- return
- }
- c.resume(FingerprintAuthAttemptModel.Error(errorCode, errString.toString()))
- }
-
- override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
- super.onAuthenticationSucceeded(result)
- if (c.isCompleted) {
- Log.d(TAG, "framework sent down onAuthError after finish")
- return
- }
- c.resume(FingerprintAuthAttemptModel.Success(result.fingerprint?.biometricId ?: -1))
- }
- }
-
- val cancellationSignal = CancellationSignal()
- c.invokeOnCancellation { cancellationSignal.cancel() }
- fingerprintManager?.authenticate(
- null,
- cancellationSignal,
- authenticationCallback,
- null,
- applicationContext.userId,
- )
- }
-}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintSensorInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintSensorInteractor.kt
index 073629c..7b1d4fd 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintSensorInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintSensorInteractor.kt
@@ -20,9 +20,7 @@
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import kotlinx.coroutines.flow.Flow
-/**
- * Interactor that propagates the type of [FingerprintSensor] this device supports.
- */
+/** Interactor that propagates the type of [FingerprintSensor] this device supports. */
interface FingerprintSensorInteractor {
/** Get the [FingerprintSensor] */
val fingerprintSensor: Flow<FingerprintSensor>
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/GenerateChallengeInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/GenerateChallengeInteractorImpl.kt
new file mode 100644
index 0000000..a2080fb
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/GenerateChallengeInteractorImpl.kt
@@ -0,0 +1,48 @@
+/*
+ * 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 android.content.Intent
+import android.hardware.fingerprint.FingerprintManager
+import com.android.settings.biometrics.GatekeeperPasswordProvider
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
+import com.android.settings.password.ChooseLockSettingsHelper
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+class GenerateChallengeInteractorImpl(
+ private val fingerprintManager: FingerprintManager,
+ private val userId: Int,
+ private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
+) : GenerateChallengeInteractor {
+
+ override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
+ suspendCoroutine {
+ val callback =
+ FingerprintManager.GenerateChallengeCallback { _, userId, challenge ->
+ val intent = Intent()
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gateKeeperPasswordHandle)
+ val challengeToken =
+ gatekeeperPasswordProvider.requestGatekeeperHat(intent, challenge, userId)
+
+ gatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, false)
+ val p = Pair(challenge, challengeToken)
+ it.resume(p)
+ }
+ fingerprintManager.generateChallenge(userId, callback)
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/RemoveFingerprintsInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/RemoveFingerprintsInteractorImpl.kt
new file mode 100644
index 0000000..4232963
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/RemoveFingerprintsInteractorImpl.kt
@@ -0,0 +1,55 @@
+/*
+ * 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 android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintManager.RemovalCallback
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RemoveFingerprintInteractor
+import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+class RemoveFingerprintsInteractorImpl(
+ private val fingerprintManager: FingerprintManager,
+ private val userId: Int,
+) : RemoveFingerprintInteractor {
+
+ override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
+ val callback =
+ object : RemovalCallback() {
+ override fun onRemovalError(
+ fp: android.hardware.fingerprint.Fingerprint,
+ errMsgId: Int,
+ errString: CharSequence,
+ ) {
+ it.resume(false)
+ }
+
+ override fun onRemovalSucceeded(
+ fp: android.hardware.fingerprint.Fingerprint?,
+ remaining: Int,
+ ) {
+ it.resume(true)
+ }
+ }
+ fingerprintManager.remove(
+ android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId),
+ userId,
+ callback,
+ )
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/RenameFingerprintsInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/RenameFingerprintsInteractorImpl.kt
new file mode 100644
index 0000000..f238e7c
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/RenameFingerprintsInteractorImpl.kt
@@ -0,0 +1,34 @@
+/*
+ * 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 android.hardware.fingerprint.FingerprintManager
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.RenameFingerprintInteractor
+import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+class RenameFingerprintsInteractorImpl(
+ private val fingerprintManager: FingerprintManager,
+ private val userId: Int,
+ private val backgroundDispatcher: CoroutineDispatcher,
+) : RenameFingerprintInteractor {
+
+ override suspend fun renameFingerprint(fp: FingerprintData, newName: String) {
+ withContext(backgroundDispatcher) { fingerprintManager.rename(fp.fingerId, userId, newName) }
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/SensorInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/SensorInteractorImpl.kt
new file mode 100644
index 0000000..7df0795
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/SensorInteractorImpl.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.FingerprintSensorRepository
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
+import kotlinx.coroutines.flow.Flow
+
+class SensorInteractorImpl(private val repo: FingerprintSensorRepository) :
+ SensorInteractor {
+ override val sensorPropertiesInternal = repo.fingerprintSensor
+ override val hasSideFps: Flow<Boolean> = repo.hasSideFps
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/TouchEventInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/TouchEventInteractor.kt
index 4ef2afa..778837d 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/TouchEventInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/TouchEventInteractor.kt
@@ -24,4 +24,3 @@
/** A flow simulating user touches. */
val touchEvent: Flow<MotionEvent>
}
-
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/AndroidManifest.xml b/src/com/android/settings/biometrics/fingerprint2/lib/AndroidManifest.xml
index 250f0af..0b7ea28 100644
--- a/src/com/android/settings/biometrics/fingerprint2/lib/AndroidManifest.xml
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/AndroidManifest.xml
@@ -13,6 +13,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+<manifest
package="com.android.settings.biometrics.fingerprint2.lib">
</manifest>
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/AuthenitcateInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/AuthenitcateInteractor.kt
new file mode 100644
index 0000000..4fc9413
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/AuthenitcateInteractor.kt
@@ -0,0 +1,25 @@
+/*
+ * 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 com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
+
+/** Interactor responsible for coordinating authentication. */
+interface AuthenitcateInteractor {
+ /** Runs the authenticate flow */
+ suspend fun authenticate(): FingerprintAuthAttemptModel
+}
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
new file mode 100644
index 0000000..11a9258
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/CanEnrollFingerprintsInteractor.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.lib.domain.interactor
+
+import kotlinx.coroutines.flow.Flow
+
+/** Returns whether or not a user can enroll a fingerprint */
+interface CanEnrollFingerprintsInteractor {
+ /** 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
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/EnrollFingerprintInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/EnrollFingerprintInteractor.kt
new file mode 100644
index 0000000..be7b4d0
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/EnrollFingerprintInteractor.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint2.lib.domain.interactor
+
+import android.hardware.fingerprint.FingerprintEnrollOptions
+import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
+import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
+import kotlinx.coroutines.flow.Flow
+
+/** Interactor that enrolls a fingerprint */
+interface EnrollFingerprintInteractor {
+ /**
+ * Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
+ * enrollment. If successful data in the [fingerprintEnrollState] should be populated.
+ */
+ suspend fun enroll(
+ hardwareAuthToken: ByteArray?,
+ enrollReason: EnrollReason,
+ fingerprintEnrollOptions: FingerprintEnrollOptions,
+ ): Flow<FingerEnrollState>
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/EnrolledFingerprintsInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/EnrolledFingerprintsInteractor.kt
new file mode 100644
index 0000000..14fc1e5
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/EnrolledFingerprintsInteractor.kt
@@ -0,0 +1,26 @@
+/*
+ * 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 com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
+import kotlinx.coroutines.flow.Flow
+
+/** Interface to obtain the enrolled fingerprints */
+interface EnrolledFingerprintsInteractor {
+ /** Returns the list of current fingerprints. */
+ val enrolledFingerprints: Flow<List<FingerprintData>?>
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt
deleted file mode 100644
index 5f4ceca..0000000
--- a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.biometrics.fingerprint2.lib.domain.interactor
-
-import android.hardware.fingerprint.FingerprintEnrollOptions
-import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
-import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
-import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
-import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
-import com.android.systemui.biometrics.shared.model.FingerprintSensor
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Interface to obtain the necessary data for FingerprintEnrollment/Settings
- *
- * Note that this interface should not have dependencies on heavyweight libraries such as the
- * framework, hidl/aidl, etc. This makes it much easier to test and create fakes for.
- */
-interface FingerprintManagerInteractor {
- /** Returns the list of current fingerprints. */
- val enrolledFingerprints: Flow<List<FingerprintData>?>
-
- /** Returns the max enrollable fingerprints, note during SUW this might be 1 */
- val maxEnrollableFingerprints: Flow<Int>
-
- /** Returns true if a user can enroll a fingerprint false otherwise. */
- val canEnrollFingerprints: Flow<Boolean>
-
- /** Retrieves the sensor properties of a device */
- val sensorPropertiesInternal: Flow<FingerprintSensor?>
-
- /** Runs the authenticate flow */
- suspend fun authenticate(): FingerprintAuthAttemptModel
-
- /**
- * Generates a challenge with the provided [gateKeeperPasswordHandle] and on success returns a
- * challenge and challenge token. This info can be used for secure operations such as enrollment
- *
- * @param gateKeeperPasswordHandle GateKeeper password handle generated by a Confirm
- * @return A [Pair] of the challenge and challenge token
- */
- suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray>
-
- /**
- * Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
- * enrollment. If successful data in the [fingerprintEnrollState] should be populated.
- */
- suspend fun enroll(
- hardwareAuthToken: ByteArray?,
- enrollReason: EnrollReason,
- fingerprintEnrollOptions: FingerprintEnrollOptions,
- ): Flow<FingerEnrollState>
-
- /**
- * Removes the given fingerprint, returning true if it was successfully removed and false
- * otherwise
- */
- suspend fun removeFingerprint(fp: FingerprintData): Boolean
-
- /** Renames the given fingerprint if one exists */
- suspend fun renameFingerprint(fp: FingerprintData, newName: String)
-
- /** Indicates if the device has side fingerprint */
- suspend fun hasSideFps(): Boolean?
-}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/GenerateChallengeInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/GenerateChallengeInteractor.kt
new file mode 100644
index 0000000..82667fe
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/GenerateChallengeInteractor.kt
@@ -0,0 +1,29 @@
+/*
+ * 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
+
+/** This interactor is responsible for generating a challenge. */
+interface GenerateChallengeInteractor {
+ /**
+ * Generates a challenge with the provided [gateKeeperPasswordHandle] and on success returns a
+ * challenge and challenge token. This info can be used for secure operations such as enrollment
+ *
+ * @param gateKeeperPasswordHandle GateKeeper password handle generated by a Confirm
+ * @return A [Pair] of the challenge and challenge token
+ */
+ suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray>
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/RemoveFingerprintInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/RemoveFingerprintInteractor.kt
new file mode 100644
index 0000000..6d0e5641
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/RemoveFingerprintInteractor.kt
@@ -0,0 +1,28 @@
+/*
+ * 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 com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
+
+/** Interactor in charge of removing a fingerprint */
+interface RemoveFingerprintInteractor {
+ /**
+ * Removes the given fingerprint, returning true if it was successfully removed and false
+ * otherwise
+ */
+ suspend fun removeFingerprint(fp: FingerprintData): Boolean
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/RenameFingerprintInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/RenameFingerprintInteractor.kt
new file mode 100644
index 0000000..d7fe1c0
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/RenameFingerprintInteractor.kt
@@ -0,0 +1,25 @@
+/*
+ * 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 com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
+
+/** Interactor that can rename a fingerprint. */
+interface RenameFingerprintInteractor {
+ /** Renames the given fingerprint if one exists */
+ suspend fun renameFingerprint(fp: FingerprintData, newName: String)
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/SensorInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/SensorInteractor.kt
new file mode 100644
index 0000000..f265c32
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/SensorInteractor.kt
@@ -0,0 +1,28 @@
+/*
+ * 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 com.android.systemui.biometrics.shared.model.FingerprintSensor
+import kotlinx.coroutines.flow.Flow
+
+/** Interactor that has various information about a fingerprint sensor */
+interface SensorInteractor {
+ /** Retrieves the sensor properties of the device */
+ val sensorPropertiesInternal: Flow<FingerprintSensor?>
+ /** Indicates if the device supports side fps */
+ val hasSideFps: Flow<Boolean>
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
index 421548f..77d070e 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
@@ -96,8 +96,8 @@
}
/**
- * View models below this line are not used by this class but must be initialized
- * in the activity view model store to be used by other view models.
+ * View models below this line are not used by this class but must be initialized in the activity
+ * view model store to be used by other view models.
*/
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel by viewModels {
FingerprintEnrollViewModel.Factory
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
index c95020d..932c408 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
@@ -25,7 +25,7 @@
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
@@ -46,10 +46,10 @@
/** View Model used by the rear fingerprint enrollment fragment. */
class RFPSViewModel(
- private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
- private val navigationViewModel: FingerprintNavigationViewModel,
- orientationInteractor: OrientationInteractor,
- private val fingerprintManager: FingerprintManagerInteractor,
+ private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
+ private val navigationViewModel: FingerprintNavigationViewModel,
+ orientationInteractor: OrientationInteractor,
+ private val sensorInteractor: SensorInteractor,
) : ViewModel() {
private val _textViewIsVisible = MutableStateFlow(false)
@@ -62,7 +62,7 @@
val shouldAnimateIcon = _shouldAnimateIcon
private var enrollFlow: Flow<FingerEnrollState?> =
- fingerprintManager.sensorPropertiesInternal.filterNotNull().combine(
+ sensorInteractor.sensorPropertiesInternal.filterNotNull().combine(
fingerprintEnrollViewModel.enrollFlow
) { props, enroll ->
if (props.sensorType == FingerprintSensorType.REAR) {
@@ -181,7 +181,7 @@
provider[FingerprintEnrollEnrollingViewModel::class],
provider[FingerprintNavigationViewModel::class],
biometricEnvironment.orientationInteractor,
- biometricEnvironment.fingerprintManagerInteractor,
+ biometricEnvironment.createSensorPropertiesInteractor(),
)
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
index 3396cdc..658c6c7 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
@@ -38,7 +38,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.VibrationInteractor
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText
@@ -76,17 +76,17 @@
enrollStageInteractor: EnrollStageInteractor,
orientationInteractor: OrientationInteractor,
udfpsEnrollInteractor: UdfpsEnrollInteractor,
- fingerprintManager: FingerprintManagerInteractor,
accessibilityInteractor: AccessibilityInteractor,
sensorRepository: FingerprintSensorInteractor,
touchEventInteractor: TouchEventInteractor,
+ sensorInteractor: SensorInteractor,
) : ViewModel() {
private val isSetupWizard = flowOf(false)
private var shouldResetErollment = false
private var _enrollState: Flow<FingerEnrollState?> =
- fingerprintManager.sensorPropertiesInternal.filterNotNull().combine(
+ sensorInteractor.sensorPropertiesInternal.filterNotNull().combine(
fingerprintEnrollEnrollingViewModel.enrollFlow
) { props, enroll ->
if (props.sensorType.isUdfps()) {
@@ -198,8 +198,7 @@
.distinctUntilChanged()
private val _touchEvent: MutableStateFlow<MotionEvent?> = MutableStateFlow(null)
- val touchEvent =
- _touchEvent.asStateFlow().filterNotNull()
+ val touchEvent = _touchEvent.asStateFlow().filterNotNull()
/** Determines the current [EnrollStageModel] enrollment is in */
private val enrollStage: Flow<EnrollStageModel> =
@@ -267,11 +266,7 @@
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
}
- viewModelScope.launch {
- touchEventInteractor.touchEvent.collect {
- _touchEvent.update { it }
- }
- }
+ viewModelScope.launch { touchEventInteractor.touchEvent.collect { _touchEvent.update { it } } }
}
/** Indicates if we should show the lottie. */
@@ -430,10 +425,10 @@
biometricEnvironment.enrollStageInteractor,
biometricEnvironment.orientationInteractor,
biometricEnvironment.udfpsEnrollInteractor,
- biometricEnvironment.fingerprintManagerInteractor,
biometricEnvironment.accessibilityInteractor,
biometricEnvironment.sensorInteractor,
biometricEnvironment.touchEventInteractor,
+ biometricEnvironment.createSensorPropertiesInteractor(),
)
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt
index 5ce2ed7..0803f89 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt
@@ -16,27 +16,27 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
-import android.util.Log
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
import kotlinx.coroutines.flow.Flow
/** Models the UI state for [FingerprintEnrollConfirmationV2Fragment] */
class FingerprintEnrollConfirmationViewModel(
private val navigationViewModel: FingerprintNavigationViewModel,
- fingerprintInteractor: FingerprintManagerInteractor,
+ private val canEnrollFingerprintsInteractor: CanEnrollFingerprintsInteractor,
) : ViewModel() {
/**
* Indicates if the add another button is possible. This should only be true when the user is able
* to enroll more fingerprints.
*/
- val isAddAnotherButtonVisible: Flow<Boolean> = fingerprintInteractor.canEnrollFingerprints
+ val isAddAnotherButtonVisible: Flow<Boolean> =
+ canEnrollFingerprintsInteractor.canEnrollFingerprints
/**
* Indicates that the user has clicked the next button and is done with fingerprint enrollment.
@@ -64,7 +64,7 @@
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollConfirmationViewModel(
provider[FingerprintNavigationViewModel::class],
- biometricEnvironment!!.fingerprintManagerInteractor,
+ biometricEnvironment!!.createCanEnrollFingerprintsInteractor(),
)
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt
index 3568dbd..9b2cdde 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt
@@ -27,7 +27,7 @@
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
@@ -44,20 +44,20 @@
/** Models the UI state for fingerprint enroll education */
class FingerprintEnrollFindSensorViewModel(
- private val navigationViewModel: FingerprintNavigationViewModel,
- private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
- private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
- backgroundViewModel: BackgroundViewModel,
- fingerprintFlowViewModel: FingerprintFlowViewModel,
- accessibilityInteractor: AccessibilityInteractor,
- foldStateInteractor: FoldStateInteractor,
- orientationInteractor: OrientationInteractor,
- fingerprintManagerInteractor: FingerprintManagerInteractor,
+ private val navigationViewModel: FingerprintNavigationViewModel,
+ private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
+ private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
+ backgroundViewModel: BackgroundViewModel,
+ fingerprintFlowViewModel: FingerprintFlowViewModel,
+ accessibilityInteractor: AccessibilityInteractor,
+ foldStateInteractor: FoldStateInteractor,
+ orientationInteractor: OrientationInteractor,
+ sensorInteractor: SensorInteractor,
) : ViewModel() {
/** Represents the stream of sensor type. */
val sensorType: Flow<FingerprintSensorType> =
- fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
+ sensorInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
private val _isUdfps: Flow<Boolean> =
sensorType.map {
it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC
@@ -216,7 +216,7 @@
biometricEnvironment.accessibilityInteractor,
biometricEnvironment.foldStateInteractor,
biometricEnvironment.orientationInteractor,
- biometricEnvironment.fingerprintManagerInteractor,
+ biometricEnvironment.createSensorPropertiesInteractor(),
)
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollIntroViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollIntroViewModel.kt
index 6ec2048..e103cbc 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollIntroViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollIntroViewModel.kt
@@ -22,7 +22,7 @@
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Introduction
import com.android.systemui.biometrics.shared.model.FingerprintSensor
@@ -30,13 +30,13 @@
/** A view model for fingerprint enroll introduction. */
class FingerprintEnrollIntroViewModel(
- val navigationViewModel: FingerprintNavigationViewModel,
- fingerprintFlowViewModel: FingerprintFlowViewModel,
- fingerprintManagerInteractor: FingerprintManagerInteractor,
+ val navigationViewModel: FingerprintNavigationViewModel,
+ fingerprintFlowViewModel: FingerprintFlowViewModel,
+ sensorInteractor: SensorInteractor,
) : ViewModel() {
/** Represents a stream of [FingerprintSensor] */
- val sensor: Flow<FingerprintSensor?> = fingerprintManagerInteractor.sensorPropertiesInternal
+ val sensor: Flow<FingerprintSensor?> = sensorInteractor.sensorPropertiesInternal
/** Represents a stream of [FingerprintFlow] */
val fingerprintFlow: Flow<FingerprintFlow?> = fingerprintFlowViewModel.fingerprintFlow
@@ -67,7 +67,7 @@
FingerprintEnrollIntroViewModel(
provider[FingerprintNavigationViewModel::class],
provider[FingerprintFlowViewModel::class],
- biometricEnvironment!!.fingerprintManagerInteractor,
+ biometricEnvironment!!.createSensorPropertiesInteractor(),
)
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt
index 2669b8b..fb8a182 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt
@@ -24,7 +24,8 @@
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
@@ -42,9 +43,10 @@
/** Represents all of the fingerprint information needed for a fingerprint enrollment process. */
class FingerprintEnrollViewModel(
- private val fingerprintManagerInteractor: FingerprintManagerInteractor,
- gatekeeperViewModel: FingerprintGatekeeperViewModel,
- val navigationViewModel: FingerprintNavigationViewModel,
+ gatekeeperViewModel: FingerprintGatekeeperViewModel,
+ val navigationViewModel: FingerprintNavigationViewModel,
+ private val sensorInteractor: SensorInteractor,
+ private val fingerprintEnrollInteractor: EnrollFingerprintInteractor,
) : ViewModel() {
/**
@@ -67,7 +69,7 @@
/** Represents the stream of [FingerprintSensorType] */
val sensorType: Flow<FingerprintSensorType?> =
- fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
+ sensorInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
/**
* A flow that contains a [FingerprintEnrollViewModel] which contains the relevant information for
@@ -90,7 +92,7 @@
enrollReason != null &&
enrollOptions != null
) {
- fingerprintManagerInteractor
+ fingerprintEnrollInteractor
.enroll(hardwareAuthToken.token, enrollReason, enrollOptions)
.collect { emit(it) }
}
@@ -137,9 +139,10 @@
val biometricEnvironment = settingsApplication.biometricEnvironment
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollViewModel(
- biometricEnvironment!!.fingerprintManagerInteractor,
provider[FingerprintGatekeeperViewModel::class],
provider[FingerprintNavigationViewModel::class],
+ biometricEnvironment!!.createSensorPropertiesInteractor(),
+ biometricEnvironment!!.createFingerprintEnrollInteractor(),
)
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt
index b5be165..c2b0a0f 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt
@@ -24,7 +24,7 @@
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -50,7 +50,7 @@
* in as a parameter to this class.
*/
class FingerprintGatekeeperViewModel(
- private val fingerprintManagerInteractor: FingerprintManagerInteractor
+ private val generateChallengeInteractor: GenerateChallengeInteractor
) : ViewModel() {
private var _gatekeeperInfo: MutableStateFlow<GatekeeperInfo?> = MutableStateFlow(null)
@@ -78,7 +78,7 @@
_gatekeeperInfo.update { GatekeeperInfo.Invalid }
} else {
viewModelScope.launch {
- val res = fingerprintManagerInteractor.generateChallenge(theGatekeeperPasswordHandle!!)
+ val res = generateChallengeInteractor.generateChallenge(theGatekeeperPasswordHandle!!)
_gatekeeperInfo.update { GatekeeperInfo.GatekeeperPasswordInfo(res.second, res.first) }
if (shouldStartTimer) {
startTimeout()
@@ -119,7 +119,7 @@
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment
- FingerprintGatekeeperViewModel(biometricEnvironment!!.fingerprintManagerInteractor)
+ FingerprintGatekeeperViewModel(biometricEnvironment!!.createGenerateChallengeInteractor())
}
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt
index caf7d2a..d9bcf7f 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt
@@ -23,7 +23,7 @@
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.SensorInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Finish
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep
@@ -46,7 +46,7 @@
* fragments/viewmodels that want to consume these events. It should provide no additional
* functionality beyond what is available in [FingerprintNavigationStep].
*/
-class FingerprintNavigationViewModel(fingerprintManagerInteractor: FingerprintManagerInteractor) :
+class FingerprintNavigationViewModel(sensorInteractor: SensorInteractor) :
ViewModel() {
private val _flowInternal: MutableStateFlow<FingerprintFlow?> = MutableStateFlow(null)
@@ -55,7 +55,7 @@
combine(
_flowInternal,
_hasConfirmedDeviceCredential,
- fingerprintManagerInteractor.sensorPropertiesInternal,
+ sensorInteractor.sensorPropertiesInternal,
) { flow, hasConfirmed, sensorType ->
if (flow == null || sensorType == null) {
return@combine null
@@ -144,7 +144,7 @@
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment
- FingerprintNavigationViewModel(biometricEnvironment!!.fingerprintManagerInteractor)
+ FingerprintNavigationViewModel(biometricEnvironment!!.createSensorPropertiesInteractor())
}
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
index 4c3773b..241eaea 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt
@@ -35,19 +35,16 @@
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
-import com.android.internal.widget.LockPatternUtils
import com.android.settings.R
+import com.android.settings.SettingsApplication
import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
import com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
-import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
-import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
-import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
@@ -223,35 +220,24 @@
val fingerprintSensorProvider =
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
- val fingerprintEnrollStateRepository =
- FingerprintEnrollInteractorImpl(
- requireContext().applicationContext,
- fingerprintManager,
- Settings,
- )
-
- val interactor =
- FingerprintManagerInteractorImpl(
- context.applicationContext,
- backgroundDispatcher,
- fingerprintManager,
- fingerprintSensorProvider,
- GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
- fingerprintEnrollStateRepository,
- )
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
val challenge = intent.getLongExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, -1L)
+ val application = requireActivity().application as SettingsApplication
+ val environment =
+ application.biometricEnvironment
+ ?: throw IllegalStateException("The biometric environment must be present")
navigationViewModel =
ViewModelProvider(
this,
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
userId,
- interactor,
backgroundDispatcher,
token,
challenge,
+ environment.createFingerprintsEnrolledInteractor(),
+ environment.createGenerateChallengeInteractor(),
),
)[FingerprintSettingsNavigationViewModel::class.java]
@@ -260,9 +246,14 @@
this,
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
userId,
- interactor,
backgroundDispatcher,
navigationViewModel,
+ environment.createCanEnrollFingerprintsInteractor(),
+ environment.createSensorPropertiesInteractor(),
+ environment.createAuthenticateInteractor(),
+ environment.createRenameFingerprintInteractor(),
+ environment.createRemoveFingerprintInteractor(),
+ environment.createFingerprintsEnrolledInteractor(),
),
)[FingerprintSettingsViewModel::class.java]
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsNavigationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsNavigationViewModel.kt
index 8a694ae..73b2b1c 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsNavigationViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsNavigationViewModel.kt
@@ -21,7 +21,8 @@
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.BiometricEnrollBase
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -33,10 +34,11 @@
/** A Viewmodel that represents the navigation of the FingerprintSettings activity. */
class FingerprintSettingsNavigationViewModel(
private val userId: Int,
- private val fingerprintManagerInteractor: FingerprintManagerInteractor,
private val backgroundDispatcher: CoroutineDispatcher,
tokenInit: ByteArray?,
challengeInit: Long?,
+ private val enrolledFingerprintsInteractor: EnrolledFingerprintsInteractor,
+ private val generateChallengeInteractor: GenerateChallengeInteractor,
) : ViewModel() {
private var token = tokenInit
@@ -52,7 +54,7 @@
_nextStep.update { LaunchConfirmDeviceCredential(userId) }
} else {
viewModelScope.launch {
- if (fingerprintManagerInteractor.enrolledFingerprints.last()?.isEmpty() == true) {
+ if (enrolledFingerprintsInteractor.enrolledFingerprints.last()?.isEmpty() == true) {
_nextStep.update { EnrollFirstFingerprint(userId, null, challenge, token) }
} else {
showSettingsHelper()
@@ -148,13 +150,13 @@
}
private suspend fun launchEnrollNextStep(gateKeeperPasswordHandle: Long?) {
- fingerprintManagerInteractor.enrolledFingerprints.collect {
+ enrolledFingerprintsInteractor.enrolledFingerprints.collect {
if (it?.isEmpty() == true) {
_nextStep.update { EnrollFirstFingerprint(userId, gateKeeperPasswordHandle, null, null) }
} else {
viewModelScope.launch(backgroundDispatcher) {
val challengePair =
- fingerprintManagerInteractor.generateChallenge(gateKeeperPasswordHandle!!)
+ generateChallengeInteractor.generateChallenge(gateKeeperPasswordHandle!!)
challenge = challengePair.first
token = challengePair.second
@@ -174,10 +176,11 @@
class FingerprintSettingsNavigationModelFactory(
private val userId: Int,
- private val interactor: FingerprintManagerInteractor,
private val backgroundDispatcher: CoroutineDispatcher,
private val token: ByteArray?,
private val challenge: Long?,
+ private val enrolledFingerprintsInteractor: EnrolledFingerprintsInteractor,
+ private val generateChallengeInteractor: GenerateChallengeInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@@ -185,10 +188,11 @@
return FingerprintSettingsNavigationViewModel(
userId,
- interactor,
backgroundDispatcher,
token,
challenge,
+ enrolledFingerprintsInteractor,
+ generateChallengeInteractor,
)
as T
}
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 cf8c527..c306c78 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
@@ -21,7 +21,12 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
+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.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
@@ -49,9 +54,14 @@
/** Models the UI state for fingerprint settings. */
class FingerprintSettingsViewModel(
private val userId: Int,
- private val fingerprintManagerInteractor: FingerprintManagerInteractor,
private val backgroundDispatcher: CoroutineDispatcher,
private val navigationViewModel: FingerprintSettingsNavigationViewModel,
+ private val canEnrollFingerprintsInteractor: CanEnrollFingerprintsInteractor,
+ private val sensorInteractor: SensorInteractor,
+ private val authenticateInteractor: AuthenitcateInteractor,
+ private val renameFingerprintInteractor: RenameFingerprintInteractor,
+ private val removeFingerprintInteractor: RemoveFingerprintInteractor,
+ private val enrolledFingerprintsInteractor: EnrolledFingerprintsInteractor,
) : ViewModel() {
private val _enrolledFingerprints: MutableStateFlow<List<FingerprintData>?> =
MutableStateFlow(null)
@@ -62,19 +72,18 @@
/** Represents the stream of the information of "Add Fingerprint" preference. */
val addFingerprintPrefInfo: Flow<Pair<Boolean, Int>> =
- _enrolledFingerprints.filterOnlyWhenSettingsIsShown().transform {
- emit(
- Pair(
- fingerprintManagerInteractor.canEnrollFingerprints.first(),
- fingerprintManagerInteractor.maxEnrollableFingerprints.first(),
- )
- )
+ _enrolledFingerprints.filterOnlyWhenSettingsIsShown().combine(
+ canEnrollFingerprintsInteractor.canEnrollFingerprints
+ ) { _, canEnrollFingerprints ->
+ Pair(canEnrollFingerprints, canEnrollFingerprintsInteractor.maxFingerprintsEnrollable())
}
/** Represents the stream of visibility of sfps preference. */
val isSfpsPrefVisible: Flow<Boolean> =
- _enrolledFingerprints.filterOnlyWhenSettingsIsShown().transform {
- emit(fingerprintManagerInteractor.hasSideFps() == true && !it.isNullOrEmpty())
+ _enrolledFingerprints.filterOnlyWhenSettingsIsShown().combine(sensorInteractor.hasSideFps) {
+ fingerprints,
+ hasSideFps ->
+ hasSideFps && !fingerprints.isNullOrEmpty()
}
private val _isShowingDialog: MutableStateFlow<PreferenceViewModel?> = MutableStateFlow(null)
@@ -90,10 +99,10 @@
private val _consumerShouldAuthenticate: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val _fingerprintSensorType: Flow<FingerprintSensorType> =
- fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
+ sensorInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
private val _sensorNullOrEmpty: Flow<Boolean> =
- fingerprintManagerInteractor.sensorPropertiesInternal.map { it == null }
+ sensorInteractor.sensorPropertiesInternal.map { it == null }
private val _isLockedOut: MutableStateFlow<FingerprintAuthAttemptModel.Error?> =
MutableStateFlow(null)
@@ -172,7 +181,7 @@
while (it && navigationViewModel.nextStep.value is ShowSettings) {
Log.d(TAG, "canAuthenticate authing")
attemptingAuth()
- when (val authAttempt = fingerprintManagerInteractor.authenticate()) {
+ when (val authAttempt = authenticateInteractor.authenticate()) {
is FingerprintAuthAttemptModel.Success -> {
onAuthSuccess(authAttempt)
emit(authAttempt)
@@ -243,7 +252,7 @@
/** A request to delete a fingerprint */
fun deleteFingerprint(fp: FingerprintData) {
viewModelScope.launch(backgroundDispatcher) {
- if (fingerprintManagerInteractor.removeFingerprint(fp)) {
+ if (removeFingerprintInteractor.removeFingerprint(fp)) {
updateEnrolledFingerprints()
}
}
@@ -252,7 +261,7 @@
/** A request to rename a fingerprint */
fun renameFingerprint(fp: FingerprintData, newName: String) {
viewModelScope.launch {
- fingerprintManagerInteractor.renameFingerprint(fp, newName)
+ renameFingerprintInteractor.renameFingerprint(fp, newName)
updateEnrolledFingerprints()
}
}
@@ -271,7 +280,7 @@
}
private suspend fun updateEnrolledFingerprints() {
- _enrolledFingerprints.update { fingerprintManagerInteractor.enrolledFingerprints.first() }
+ _enrolledFingerprints.update { enrolledFingerprintsInteractor.enrolledFingerprints.first() }
}
/** Used to indicate whether the consumer of the view model is ready for authentication. */
@@ -288,9 +297,14 @@
class FingerprintSettingsViewModelFactory(
private val userId: Int,
- private val interactor: FingerprintManagerInteractor,
private val backgroundDispatcher: CoroutineDispatcher,
private val navigationViewModel: FingerprintSettingsNavigationViewModel,
+ private val canEnrollFingerprintsInteractor: CanEnrollFingerprintsInteractor,
+ private val sensorInteractor: SensorInteractor,
+ private val authenticateInteractor: AuthenitcateInteractor,
+ private val renameFingerprintInteractor: RenameFingerprintInteractor,
+ private val removeFingerprintInteractor: RemoveFingerprintInteractor,
+ private val enrolledFingerprintsInteractor: EnrolledFingerprintsInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@@ -298,9 +312,14 @@
return FingerprintSettingsViewModel(
userId,
- interactor,
backgroundDispatcher,
navigationViewModel,
+ canEnrollFingerprintsInteractor,
+ sensorInteractor,
+ authenticateInteractor,
+ renameFingerprintInteractor,
+ removeFingerprintInteractor,
+ enrolledFingerprintsInteractor,
)
as T
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java
index 4c17a7c..958740b 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java
@@ -129,6 +129,10 @@
private Dialog getUnsupportedDialog() {
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
.setTitle(getString(R.string.audio_streams_dialog_cannot_listen))
+ .setSubTitle1(
+ mBroadcastMetadata != null
+ ? AudioStreamsHelper.getBroadcastName(mBroadcastMetadata)
+ : "")
.setSubTitle2(getString(R.string.audio_streams_dialog_unsupported_device_subtitle))
.setRightButtonText(getString(R.string.audio_streams_dialog_close))
.setRightButtonOnClickListener(
diff --git a/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java b/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java
index f918d26..04ae92b 100644
--- a/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java
+++ b/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java
@@ -17,8 +17,10 @@
package com.android.settings.development;
import android.content.Context;
-import android.provider.Settings;
+import android.hardware.input.InputSettings;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
@@ -26,23 +28,13 @@
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import android.hardware.input.InputSettings;
-
-/** PreferenceController that controls the "Touchpad visualizer" developer option. */
+/** PreferenceController that controls the "Show touchpad input" developer option. */
public class TouchpadVisualizerPreferenceController extends
DeveloperOptionsPreferenceController implements
Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
private static final String TOUCHPAD_VISUALIZER_KEY = "touchpad_visualizer";
- @VisibleForTesting
- static final int SETTING_VALUE_ON = 1;
- @VisibleForTesting
- static final int SETTING_VALUE_OFF = 0;
-
public TouchpadVisualizerPreferenceController(@NonNull Context context) {
super(context);
}
@@ -60,24 +52,22 @@
@Override
public boolean onPreferenceChange(@NonNull Preference preference, @Nullable Object newValue) {
final boolean isEnabled = newValue != null ? (Boolean) newValue : false;
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.TOUCHPAD_VISUALIZER,
- isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+ InputSettings.setTouchpadVisualizer(mContext, isEnabled);
+
return true;
}
@Override
public void updateState(@NonNull Preference preference) {
- int touchpadVisualizer = Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.TOUCHPAD_VISUALIZER, SETTING_VALUE_OFF);
- ((SwitchPreference) mPreference).setChecked(touchpadVisualizer != SETTING_VALUE_OFF);
+ boolean touchpadVisualizerEnabled = InputSettings.useTouchpadVisualizer(mContext);
+ ((SwitchPreference) mPreference).setChecked(touchpadVisualizerEnabled);
}
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.TOUCHPAD_VISUALIZER,
- SETTING_VALUE_OFF);
+ InputSettings.setTouchpadVisualizer(mContext, false);
+
((SwitchPreference) mPreference).setChecked(false);
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index 59dc35e..e4f17e2 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -24,10 +24,10 @@
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.UiccCardInfo
-import android.telephony.UiccSlotInfo
import android.util.Log
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
import com.android.settings.network.telephony.MobileDataRepository
+import com.android.settings.network.telephony.UiccSlotRepository
import com.android.settings.sim.SimActivationNotifier
import com.android.settings.spa.network.setDefaultData
import com.android.settings.spa.network.setDefaultSms
@@ -46,7 +46,6 @@
var targetSubInfo: SubscriptionInfo? = null
var availableSubInfoList: List<SubscriptionInfo> = listOf()
var activeSubInfoList: List<SubscriptionInfo> = listOf()
- var slotInfoList: List<UiccSlotInfo> = listOf()
var uiccCardInfoList: List<UiccCardInfo> = listOf()
var targetPrimarySimCalls: Int = INVALID_SUBSCRIPTION_ID
var targetPrimarySimTexts: Int = INVALID_SUBSCRIPTION_ID
@@ -73,14 +72,6 @@
}
return uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported }
}
- var isRemovablePsimProfileEnabled: Boolean = false
- get() {
- if(slotInfoList.isEmpty()) {
- Log.w(TAG, "UICC Slot info list is empty.")
- return false
- }
- return UiccSlotUtil.isRemovableSimEnabled(slotInfoList)
- }
var isEsimProfileEnabled: Boolean = false
get() {
activeSubInfoList.stream().anyMatch { it.isEmbedded }
@@ -137,19 +128,11 @@
return telephonyManager?.doesSwitchMultiSimConfigTriggerReboot() ?: false
}
- fun isValid(): Boolean {
- return targetSubId != INVALID_SUBSCRIPTION_ID
- && targetSubInfo != null
- && activeSubInfoList.isNotEmpty()
- && slotInfoList.isNotEmpty()
- }
-
fun clear() {
targetSubId = -1
targetSubInfo = null
availableSubInfoList = listOf()
activeSubInfoList = listOf()
- slotInfoList = listOf()
uiccCardInfoList = listOf()
targetPrimarySimCalls = -1
targetPrimarySimTexts = -1
@@ -181,8 +164,6 @@
availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
targetSubInfo?.let { userSelectedSubInfoList.add(it) }
Log.d(TAG, "targetSubId: $targetSubId , targetSubInfo: $targetSubInfo")
- slotInfoList = telephonyManager?.uiccSlotsInfo?.toList() ?: listOf()
- Log.d(TAG, "slotInfoList: $slotInfoList.")
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
@@ -192,7 +173,6 @@
Log.d(
TAG,"doesTargetSimHaveEsimOperation: $doesTargetSimHaveEsimOperation" +
- ", isRemovableSimEnabled: $isRemovablePsimProfileEnabled" +
", isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported" +
", targetPrimarySimCalls: $targetPrimarySimCalls" +
", targetPrimarySimTexts: $targetPrimarySimTexts" +
@@ -317,14 +297,15 @@
return true
}
- if (doesTargetSimHaveEsimOperation && isRemovablePsimProfileEnabled) {
- Log.d(TAG,
- "eSIM operation and removable PSIM is enabled. DSDS condition satisfied."
- )
- return true
- }
-
- if (!doesTargetSimHaveEsimOperation && isEsimProfileEnabled) {
+ if (doesTargetSimHaveEsimOperation) {
+ if (UiccSlotRepository(telephonyManager).anyRemovablePhysicalSimEnabled()) {
+ Log.d(
+ TAG,
+ "eSIM operation and removable PSIM is enabled. DSDS condition satisfied."
+ )
+ return true
+ }
+ } else if (isEsimProfileEnabled) {
Log.d(TAG,
"Removable SIM operation and eSIM profile is enabled. DSDS condition"
+ " satisfied."
diff --git a/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java b/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java
index a26aa8a..8f6c32a 100644
--- a/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java
+++ b/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java
@@ -30,8 +30,6 @@
import com.android.settings.SidecarFragment;
import com.android.settings.network.telephony.EuiccOperationSidecar;
-import com.google.common.collect.ImmutableList;
-
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
@@ -205,10 +203,10 @@
}
private int getLogicalSlotIndex(int physicalSlotIndex, int portIndex) {
- ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(mTelephonyManager);
- if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.size()
- && slotInfos.get(physicalSlotIndex) != null) {
- for (UiccPortInfo portInfo : slotInfos.get(physicalSlotIndex).getPorts()) {
+ UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
+ if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length
+ && slotInfos[physicalSlotIndex] != null) {
+ for (UiccPortInfo portInfo : slotInfos[physicalSlotIndex].getPorts()) {
if (portInfo.getPortIndex() == portIndex) {
return portInfo.getLogicalSlotIndex();
}
diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java
index 5ae0a36..8a5a22c 100644
--- a/src/com/android/settings/network/UiccSlotUtil.java
+++ b/src/com/android/settings/network/UiccSlotUtil.java
@@ -33,8 +33,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.utils.ThreadUtils;
-import com.google.common.collect.ImmutableList;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -106,18 +104,6 @@
}
/**
- * Returns an immutable list of all UICC slots. If TelephonyManager#getUiccSlotsInfo returns, it
- * returns an empty list instead.
- */
- public static ImmutableList<UiccSlotInfo> getSlotInfos(TelephonyManager telMgr) {
- UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo();
- if (slotInfos == null) {
- return ImmutableList.of();
- }
- return ImmutableList.copyOf(slotInfos);
- }
-
- /**
* Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
* INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
*
@@ -219,14 +205,13 @@
*/
public static int getEsimSlotId(Context context, int subId) {
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
- List<UiccCardInfo> uiccCardInfos = telMgr.getUiccCardsInfo();
- ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class).createForAllUserProfiles();
SubscriptionInfo subInfo = SubscriptionUtil.getSubById(subscriptionManager, subId);
// checking whether this is the removable esim. If it is, then return the removable slot id.
if (subInfo != null && subInfo.isEmbedded()) {
+ List<UiccCardInfo> uiccCardInfos = telMgr.getUiccCardsInfo();
for (UiccCardInfo uiccCardInfo : uiccCardInfos) {
if (uiccCardInfo.getCardId() == subInfo.getCardId()
&& uiccCardInfo.getCardId() > TelephonyManager.UNSUPPORTED_CARD_ID
@@ -238,10 +223,12 @@
}
}
- int firstEsimSlot = IntStream.range(0, slotInfos.size())
+ UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo();
+ if (slotInfos == null) return -1;
+ int firstEsimSlot = IntStream.range(0, slotInfos.length)
.filter(
index -> {
- UiccSlotInfo slotInfo = slotInfos.get(index);
+ UiccSlotInfo slotInfo = slotInfos[index];
if (slotInfo == null) {
return false;
}
@@ -421,41 +408,6 @@
.orElse(INVALID_LOGICAL_SLOT_ID);
}
- /**
- * Return whether the removable psim is enabled.
- *
- * @param telMgr is a TelephonyManager.
- * @return whether the removable psim is enabled.
- */
- public static boolean isRemovableSimEnabled(TelephonyManager telMgr) {
- if (telMgr == null) {
- return false;
- }
- List<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
- return isRemovableSimEnabled(slotInfos);
- }
-
- /**
- * Return whether the removable psim is enabled.
- *
- * @param slotInfos is a List of UiccSlotInfo.
- * @return whether the removable psim is enabled.
- */
- public static boolean isRemovableSimEnabled(List<UiccSlotInfo> slotInfos) {
- boolean isRemovableSimEnabled =
- slotInfos.stream()
- .anyMatch(
- slot -> slot != null
- && slot.isRemovable()
- && !slot.getIsEuicc()
- && slot.getPorts().stream()
- .anyMatch(port -> port.isActive())
- && slot.getCardStateInfo()
- == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
- Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
- return isRemovableSimEnabled;
- }
-
private static boolean isMultipleEnabledProfilesSupported(TelephonyManager telMgr) {
List<UiccCardInfo> cardInfos = telMgr.getUiccCardsInfo();
if (cardInfos == null) {
diff --git a/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt b/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
index d47a246..aa113b6 100644
--- a/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
+++ b/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
@@ -29,35 +29,32 @@
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
import com.android.settings.datausage.DataUsageUtils
import com.android.settings.datausage.lib.DataUsageFormatter.FormattedDataUsage
import com.android.settings.datausage.lib.DataUsageLib
import com.android.settings.datausage.lib.NetworkCycleDataRepository
import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.AllTimeRange
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-/**
- * Preference controller for "Data usage"
- */
+/** Preference controller for "Data usage" */
class DataUsagePreferenceController(context: Context, key: String) :
- TelephonyBasePreferenceController(context, key) {
+ BasePreferenceController(context, key) {
+ private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
private lateinit var preference: Preference
private var networkTemplate: NetworkTemplate? = null
fun init(subId: Int) {
- mSubId = subId
+ this.subId = subId
}
- override fun getAvailabilityStatus(subId: Int): Int = when {
- SubscriptionManager.isValidSubscriptionId(subId) &&
- DataUsageUtils.hasMobileData(mContext) -> AVAILABLE
-
- else -> AVAILABLE_UNSEARCHABLE
- }
+ override fun getAvailabilityStatus() = AVAILABLE
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
@@ -75,11 +72,12 @@
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
if (preference.key != preferenceKey || networkTemplate == null) return false
- val intent = Intent(Settings.ACTION_MOBILE_DATA_USAGE).apply {
- setPackage(mContext.packageName)
- putExtra(Settings.EXTRA_NETWORK_TEMPLATE, networkTemplate)
- putExtra(Settings.EXTRA_SUB_ID, mSubId)
- }
+ val intent =
+ Intent(Settings.ACTION_MOBILE_DATA_USAGE).apply {
+ setPackage(mContext.packageName)
+ putExtra(Settings.EXTRA_NETWORK_TEMPLATE, networkTemplate)
+ putExtra(Settings.EXTRA_SUB_ID, subId)
+ }
mContext.startActivity(intent)
return true
}
@@ -93,13 +91,10 @@
preference.summary = summary?.displayText
}
- private fun getNetworkTemplate(): NetworkTemplate? = when {
- SubscriptionManager.isValidSubscriptionId(mSubId) -> {
- DataUsageLib.getMobileTemplate(mContext, mSubId)
- }
-
- else -> null
- }
+ private fun getNetworkTemplate(): NetworkTemplate? =
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ DataUsageLib.getMobileTemplate(mContext, subId)
+ } else null
@VisibleForTesting
fun createNetworkCycleDataRepository(): NetworkCycleDataRepository? =
@@ -118,4 +113,16 @@
val allTimeUsage = repository.queryUsage(AllTimeRange)
return allTimeUsage.getDataUsedString(mContext) to (allTimeUsage.usage > 0)
}
+
+ companion object {
+ class DataUsageSearchItem(private val context: Context) : MobileNetworkSettingsSearchItem {
+ override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+ if (!DataUsageUtils.hasMobileData(context)) return null
+ return MobileNetworkSettingsSearchResult(
+ key = "data_usage_summary",
+ title = context.getString(R.string.app_cellular_data_usage),
+ )
+ }
+ }
+ }
}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
index 58661f0..55cb1fa 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
@@ -21,6 +21,7 @@
import android.telephony.SubscriptionInfo
import com.android.settings.R
import com.android.settings.network.SubscriptionUtil
+import com.android.settings.network.telephony.DataUsagePreferenceController.Companion.DataUsageSearchItem
import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem
import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem
import com.android.settings.network.telephony.RoamingPreferenceController.Companion.RoamingSearchItem
@@ -114,6 +115,7 @@
fun createSearchItems(context: Context): List<MobileNetworkSettingsSearchItem> =
listOf(
+ DataUsageSearchItem(context),
MmsMessageSearchItem(context),
NrAdvancedCallingSearchItem(context),
PreferredNetworkModeSearchItem(context),
diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
index 6f4d3c3..981e5bb 100644
--- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
@@ -583,7 +583,7 @@
}
private boolean isRemovableSimEnabled() {
- return UiccSlotUtil.isRemovableSimEnabled(mTelMgr);
+ return new UiccSlotRepository(mTelMgr).anyRemovablePhysicalSimEnabled();
}
private boolean isMultipleEnabledProfilesSupported() {
diff --git a/src/com/android/settings/network/telephony/UiccSlotRepository.kt b/src/com/android/settings/network/telephony/UiccSlotRepository.kt
new file mode 100644
index 0000000..3a83805
--- /dev/null
+++ b/src/com/android/settings/network/telephony/UiccSlotRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.network.telephony
+
+import android.telephony.TelephonyManager
+import android.telephony.UiccSlotInfo
+import android.util.Log
+
+class UiccSlotRepository(private val telephonyManager: TelephonyManager?) {
+
+ /** Returns whether any removable physical sim is enabled. */
+ fun anyRemovablePhysicalSimEnabled(): Boolean {
+ val result =
+ telephonyManager?.uiccSlotsInfo?.any { uiccSlotInfo: UiccSlotInfo? ->
+ uiccSlotInfo.isRemovablePhysicalSimEnabled()
+ } ?: false
+ Log.i(TAG, "anyRemovablePhysicalSimEnabled: $result")
+ return result
+ }
+
+ private fun UiccSlotInfo?.isRemovablePhysicalSimEnabled(): Boolean {
+ return this != null &&
+ isRemovable &&
+ !isEuicc &&
+ ports.any { port -> port.isActive } &&
+ cardStateInfo == UiccSlotInfo.CARD_STATE_INFO_PRESENT
+ }
+
+ companion object {
+ private const val TAG = "UiccRepository"
+ }
+}
diff --git a/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt
new file mode 100644
index 0000000..ba33257
--- /dev/null
+++ b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.network.telephony.ims
+
+import android.content.Context
+import android.telephony.AccessNetworkConstants.TransportType
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability
+import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech
+import com.android.settings.network.telephony.subscriptionsChangedFlow
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * A repository for the IMS feature.
+ *
+ * @throws IllegalArgumentException if the [subId] is invalid.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+class ImsFeatureRepository(
+ private val context: Context,
+ private val subId: Int,
+ private val provisioningRepository: ProvisioningRepository = ProvisioningRepository(context),
+ private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId)
+) {
+ /**
+ * A cold flow that determines the provisioning status for the specified IMS MmTel capability,
+ * and whether or not the requested MmTel capability is supported by the carrier on the
+ * specified network transport.
+ *
+ * @return true if the feature is provisioned and supported, false otherwise.
+ */
+ fun isReadyFlow(
+ @MmTelCapability capability: Int,
+ @ImsRegistrationTech tech: Int,
+ @TransportType transportType: Int,
+ ): Flow<Boolean> =
+ context.subscriptionsChangedFlow().flatMapLatest {
+ combine(
+ provisioningRepository.imsFeatureProvisionedFlow(subId, capability, tech),
+ imsMmTelRepository.isSupportedFlow(capability, transportType),
+ ) { imsFeatureProvisioned, isSupported ->
+ imsFeatureProvisioned && isSupported
+ }
+ }
+}
diff --git a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
index 9bc10e5..c5d1200 100644
--- a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
+++ b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
@@ -36,6 +36,7 @@
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
@@ -47,6 +48,11 @@
fun imsReadyFlow(): Flow<Boolean>
+ fun isSupportedFlow(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
+ @AccessNetworkConstants.TransportType transportType: Int,
+ ): Flow<Boolean>
+
suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
@@ -55,6 +61,11 @@
suspend fun setCrossSimCallingEnabled(enabled: Boolean)
}
+/**
+ * A repository for the IMS MMTel.
+ *
+ * @throws IllegalArgumentException if the [subId] is invalid.
+ */
class ImsMmTelRepositoryImpl(
context: Context,
private val subId: Int,
@@ -126,8 +137,12 @@
awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) }
}.catch { e ->
Log.w(TAG, "[$subId] error while imsReadyFlow", e)
+ emit(false)
}.conflate().flowOn(Dispatchers.Default)
+ override fun isSupportedFlow(capability: Int, transportType: Int): Flow<Boolean> =
+ imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) }
+
override suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
index 04e687c..6af0559 100644
--- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
+++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
@@ -20,24 +20,17 @@
import android.telephony.AccessNetworkConstants
import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL
-import android.telephony.SubscriptionManager
import android.telephony.ims.ImsMmTelManager.WiFiCallingMode
import android.telephony.ims.feature.MmTelFeature
import android.telephony.ims.stub.ImsRegistrationImplBase
import androidx.lifecycle.LifecycleOwner
+import com.android.settings.network.telephony.ims.ImsFeatureRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
-import com.android.settings.network.telephony.ims.ProvisioningRepository
-import com.android.settings.network.telephony.subscriptionsChangedFlow
import com.android.settings.network.telephony.telephonyManager
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
interface IWifiCallingRepository {
@@ -50,11 +43,11 @@
constructor(
private val context: Context,
private val subId: Int,
- private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId)
+ private val imsFeatureRepository: ImsFeatureRepository = ImsFeatureRepository(context, subId),
+ private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId),
) : IWifiCallingRepository {
private val telephonyManager = context.telephonyManager(subId)
- private val provisioningRepository = ProvisioningRepository(context)
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
@WiFiCallingMode
@@ -76,28 +69,12 @@
wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action)
}
- @OptIn(ExperimentalCoroutinesApi::class)
- fun wifiCallingReadyFlow(): Flow<Boolean> {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
- return context.subscriptionsChangedFlow().flatMapLatest {
- combine(
- provisioningRepository.imsFeatureProvisionedFlow(
- subId = subId,
- capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
- tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
- ),
- isWifiCallingSupportedFlow(),
- ) { imsFeatureProvisioned, isWifiCallingSupported ->
- imsFeatureProvisioned && isWifiCallingSupported
- }
- }
- }
-
- private fun isWifiCallingSupportedFlow(): Flow<Boolean> {
- return imsMmTelRepository.imsReadyFlow().map { imsReady ->
- imsReady && isWifiCallingSupported()
- }
- }
+ fun wifiCallingReadyFlow(): Flow<Boolean> =
+ imsFeatureRepository.isReadyFlow(
+ capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ )
suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) {
imsMmTelRepository.isSupported(
diff --git a/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java b/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java
index 81b53cc..7cc67cc 100644
--- a/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java
+++ b/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java
@@ -61,13 +61,6 @@
LayoutPreference preference = checkNotNull(screen.findPreference(getPreferenceKey()));
preference.setSelectable(false);
- if (mHeaderController == null) {
- mHeaderController = EntityHeaderController.newInstance(
- mFragment.getActivity(),
- mFragment,
- preference.findViewById(R.id.entity_header));
- }
-
ImageView iconView = checkNotNull(preference.findViewById(R.id.entity_header_icon));
ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams();
if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) {
@@ -75,6 +68,14 @@
layoutParams.height = iconSizePx;
iconView.setLayoutParams(layoutParams);
}
+
+ if (mHeaderController == null) {
+ mHeaderController = EntityHeaderController.newInstance(
+ mFragment.getActivity(),
+ mFragment,
+ preference.findViewById(R.id.entity_header));
+ mHeaderController.done(false); // Make the space for the (unused) name go away.
+ }
}
protected void updateIcon(Preference preference, @NonNull ZenMode zenMode,
diff --git a/src/com/android/settings/notification/modes/CircularIconsPreference.java b/src/com/android/settings/notification/modes/CircularIconsPreference.java
index ccf7f52..5a89352 100644
--- a/src/com/android/settings/notification/modes/CircularIconsPreference.java
+++ b/src/com/android/settings/notification/modes/CircularIconsPreference.java
@@ -16,236 +16,72 @@
package com.android.settings.notification.modes;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
import static com.google.common.base.Preconditions.checkNotNull;
import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.RestrictedPreference;
import com.google.common.base.Equivalence;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-import java.util.concurrent.Executor;
public class CircularIconsPreference extends RestrictedPreference {
- private static final float DISABLED_ITEM_ALPHA = 0.3f;
-
- record LoadedIcons(ImmutableList<Drawable> icons, int extraItems) {
- static final LoadedIcons EMPTY = new LoadedIcons(ImmutableList.of(), 0);
- }
-
- private Executor mUiExecutor;
-
- // Chronologically, fields will be set top-to-bottom.
- @Nullable private CircularIconSet<?> mIconSet;
- @Nullable private ListenableFuture<List<Drawable>> mPendingLoadIconsFuture;
- @Nullable private LoadedIcons mLoadedIcons;
+ private CircularIconSet<?> mIconSet = CircularIconSet.EMPTY;
public CircularIconsPreference(Context context) {
super(context);
- init(context);
- }
-
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- public CircularIconsPreference(Context context, Executor uiExecutor) {
- this(context);
- mUiExecutor = uiExecutor;
+ init();
}
public CircularIconsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- init(context);
+ init();
}
public CircularIconsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- init(context);
+ init();
}
public CircularIconsPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init(context);
+ init();
}
- private void init(Context context) {
- mUiExecutor = context.getMainExecutor();
+ private void init() {
setLayoutResource(R.layout.preference_circular_icons);
}
- <T> void displayIcons(CircularIconSet<T> iconSet) {
- displayIcons(iconSet, null);
+ <T> void setIcons(CircularIconSet<T> iconSet) {
+ setIcons(iconSet, null);
}
- <T> void displayIcons(CircularIconSet<T> iconSet, @Nullable Equivalence<T> itemEquivalence) {
- if (mIconSet != null && mIconSet.hasSameItemsAs(iconSet, itemEquivalence)) {
+ <T> void setIcons(CircularIconSet<T> iconSet, @Nullable Equivalence<T> itemEquivalence) {
+ if (mIconSet.hasSameItemsAs(iconSet, itemEquivalence)) {
return;
}
+
mIconSet = iconSet;
-
- mLoadedIcons = null;
- if (mPendingLoadIconsFuture != null) {
- mPendingLoadIconsFuture.cancel(true);
- mPendingLoadIconsFuture = null;
- }
-
notifyChanged();
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
+ CircularIconsView iconContainer = checkNotNull(
+ (CircularIconsView) holder.findViewById(R.id.circles_container));
- LinearLayout iconContainer = checkNotNull(
- (LinearLayout) holder.findViewById(R.id.circles_container));
- bindIconContainer(iconContainer);
- }
-
- private void bindIconContainer(LinearLayout container) {
- if (mLoadedIcons != null) {
- // We have the icons ready to display already, show them.
- setDrawables(container, mLoadedIcons);
- } else if (mIconSet != null) {
- // We know what icons we want, but haven't yet loaded them.
- if (mIconSet.size() == 0) {
- container.setVisibility(View.GONE);
- mLoadedIcons = LoadedIcons.EMPTY;
- return;
- }
- container.setVisibility(View.VISIBLE);
- if (container.getMeasuredWidth() != 0) {
- startLoadingIcons(container, mIconSet);
- } else {
- container.getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- container.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- notifyChanged();
- }
- }
- );
- }
- }
- }
-
- private void startLoadingIcons(LinearLayout container, CircularIconSet<?> iconSet) {
- Resources res = getContext().getResources();
- int availableSpace = container.getMeasuredWidth();
- int iconHorizontalSpace = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
- + res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
- int numIconsThatFit = availableSpace / iconHorizontalSpace;
-
- List<ListenableFuture<Drawable>> iconFutures;
- int extraItems;
- if (iconSet.size() > numIconsThatFit) {
- // Reserve one space for the (+xx) textview.
- int numIconsToShow = numIconsThatFit - 1;
- if (numIconsToShow < 0) {
- numIconsToShow = 0;
- }
- iconFutures = iconSet.getIcons(numIconsToShow);
- extraItems = iconSet.size() - numIconsToShow;
- } else {
- // Fit exactly or with remaining space.
- iconFutures = iconSet.getIcons();
- extraItems = 0;
- }
-
- // Display icons when all are ready (more consistent than randomly loading).
- mPendingLoadIconsFuture = Futures.allAsList(iconFutures);
- FutureUtil.whenDone(
- mPendingLoadIconsFuture,
- icons -> {
- mLoadedIcons = new LoadedIcons(ImmutableList.copyOf(icons), extraItems);
- notifyChanged(); // So that view is rebound and icons actually shown.
- },
- mUiExecutor);
- }
-
- private void setDrawables(LinearLayout container, LoadedIcons loadedIcons) {
- // Rearrange child views until we have <numImages> ImageViews...
- LayoutInflater inflater = LayoutInflater.from(getContext());
- int numImages = loadedIcons.icons.size();
- int numImageViews = getChildCount(container, ImageView.class);
- if (numImages > numImageViews) {
- for (int i = 0; i < numImages - numImageViews; i++) {
- ImageView imageView = (ImageView) inflater.inflate(
- R.layout.preference_circular_icons_item, container, false);
- container.addView(imageView, 0);
- }
- } else if (numImageViews > numImages) {
- for (int i = 0; i < numImageViews - numImages; i++) {
- container.removeViewAt(0);
- }
- }
- // ... plus 0/1 TextViews at the end.
- if (loadedIcons.extraItems > 0 && !(getLastChild(container) instanceof TextView)) {
- TextView plusView = (TextView) inflater.inflate(
- R.layout.preference_circular_icons_plus_item, container, false);
- container.addView(plusView);
- } else if (loadedIcons.extraItems == 0 && (getLastChild(container) instanceof TextView)) {
- container.removeViewAt(container.getChildCount() - 1);
- }
-
- // Show images (and +n if needed).
- for (int i = 0; i < numImages; i++) {
- ImageView imageView = (ImageView) container.getChildAt(i);
- imageView.setImageDrawable(loadedIcons.icons.get(i));
- }
- if (loadedIcons.extraItems > 0) {
- TextView textView = (TextView) checkNotNull(getLastChild(container));
- textView.setText(getContext().getString(R.string.zen_mode_plus_n_items,
- loadedIcons.extraItems));
- }
-
- // Apply enabled/disabled style.
- for (int i = 0; i < container.getChildCount(); i++) {
- View child = container.getChildAt(i);
- child.setAlpha(isEnabled() ? 1.0f : DISABLED_ITEM_ALPHA);
- }
- }
-
- private static int getChildCount(ViewGroup parent, Class<? extends View> childClass) {
- int count = 0;
- for (int i = 0; i < parent.getChildCount(); i++) {
- if (childClass.isInstance(parent.getChildAt(i))) {
- count++;
- }
- }
- return count;
- }
-
- @Nullable
- private static View getLastChild(ViewGroup parent) {
- if (parent.getChildCount() == 0) {
- return null;
- }
- return parent.getChildAt(parent.getChildCount() - 1);
- }
-
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- @Nullable
- LoadedIcons getLoadedIcons() {
- return mLoadedIcons;
+ iconContainer.setVisibility(mIconSet != null && mIconSet.size() == 0 ? GONE : VISIBLE);
+ iconContainer.setEnabled(isEnabled());
+ iconContainer.setIcons(mIconSet);
}
}
diff --git a/src/com/android/settings/notification/modes/CircularIconsView.java b/src/com/android/settings/notification/modes/CircularIconsView.java
new file mode 100644
index 0000000..b0e4280
--- /dev/null
+++ b/src/com/android/settings/notification/modes/CircularIconsView.java
@@ -0,0 +1,232 @@
+/*
+ * 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.modes;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class CircularIconsView extends LinearLayout {
+
+ private static final float DISABLED_ITEM_ALPHA = 0.3f;
+
+ record Icons(ImmutableList<Drawable> icons, int extraItems) { }
+
+ private Executor mUiExecutor;
+ private int mNumberOfCirclesThatFit;
+
+ // Chronologically, fields will be set top-to-bottom.
+ @Nullable private CircularIconSet<?> mIconSet;
+ @Nullable private ListenableFuture<List<Drawable>> mPendingLoadIconsFuture;
+ @Nullable private Icons mDisplayedIcons;
+
+ public CircularIconsView(Context context) {
+ super(context);
+ setUiExecutor(context.getMainExecutor());
+ }
+
+ public CircularIconsView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setUiExecutor(context.getMainExecutor());
+ }
+
+ public CircularIconsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setUiExecutor(context.getMainExecutor());
+ }
+
+ public CircularIconsView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setUiExecutor(context.getMainExecutor());
+ }
+
+ @VisibleForTesting
+ void setUiExecutor(Executor uiExecutor) {
+ mUiExecutor = uiExecutor;
+ }
+
+ <T> void setIcons(CircularIconSet<T> iconSet) {
+ if (mIconSet != null && mIconSet.equals(iconSet)) {
+ return;
+ }
+
+ mIconSet = checkNotNull(iconSet);
+ cancelPendingTasks();
+ if (getMeasuredWidth() != 0) {
+ startLoadingIcons(iconSet);
+ }
+ }
+
+ private void cancelPendingTasks() {
+ mDisplayedIcons = null;
+ if (mPendingLoadIconsFuture != null) {
+ mPendingLoadIconsFuture.cancel(true);
+ mPendingLoadIconsFuture = null;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ int numFitting = getNumberOfCirclesThatFit();
+ if (mNumberOfCirclesThatFit != numFitting) {
+ // View has been measured for the first time OR its dimensions have changed since then.
+ // Keep track, because we want to reload stuff if more (or less) items fit.
+ mNumberOfCirclesThatFit = numFitting;
+
+ if (mIconSet != null) {
+ cancelPendingTasks();
+ startLoadingIcons(mIconSet);
+ }
+ }
+ }
+
+ private int getNumberOfCirclesThatFit() {
+ Resources res = getContext().getResources();
+ int availableSpace = getMeasuredWidth();
+ int iconHorizontalSpace = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
+ + res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
+ return availableSpace / iconHorizontalSpace;
+ }
+
+ private void startLoadingIcons(CircularIconSet<?> iconSet) {
+ int numCirclesThatFit = getNumberOfCirclesThatFit();
+
+ List<ListenableFuture<Drawable>> iconFutures;
+ int extraItems;
+ if (iconSet.size() > numCirclesThatFit) {
+ // Reserve one space for the (+xx) textview.
+ int numIconsToShow = numCirclesThatFit - 1;
+ if (numIconsToShow < 0) {
+ numIconsToShow = 0;
+ }
+ iconFutures = iconSet.getIcons(numIconsToShow);
+ extraItems = iconSet.size() - numIconsToShow;
+ } else {
+ // Fit exactly or with remaining space.
+ iconFutures = iconSet.getIcons();
+ extraItems = 0;
+ }
+
+ // Display icons when all are ready (more consistent than randomly loading).
+ mPendingLoadIconsFuture = Futures.allAsList(iconFutures);
+ FutureUtil.whenDone(
+ mPendingLoadIconsFuture,
+ icons -> setDrawables(new Icons(ImmutableList.copyOf(icons), extraItems)),
+ mUiExecutor);
+ }
+
+ private void setDrawables(Icons icons) {
+ mDisplayedIcons = icons;
+
+ // Rearrange child views until we have <numImages> ImageViews...
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ int numImages = icons.icons.size();
+ int numImageViews = getChildCount(ImageView.class);
+ if (numImages > numImageViews) {
+ for (int i = 0; i < numImages - numImageViews; i++) {
+ ImageView imageView = (ImageView) inflater.inflate(
+ R.layout.preference_circular_icons_item, this, false);
+ addView(imageView, 0);
+ }
+ } else if (numImageViews > numImages) {
+ for (int i = 0; i < numImageViews - numImages; i++) {
+ removeViewAt(0);
+ }
+ }
+ // ... plus 0/1 TextViews at the end.
+ if (icons.extraItems > 0 && !(getLastChild() instanceof TextView)) {
+ TextView plusView = (TextView) inflater.inflate(
+ R.layout.preference_circular_icons_plus_item, this, false);
+ this.addView(plusView);
+ } else if (icons.extraItems == 0 && (getLastChild() instanceof TextView)) {
+ removeViewAt(getChildCount() - 1);
+ }
+
+ // Show images (and +n if needed).
+ for (int i = 0; i < numImages; i++) {
+ ImageView imageView = (ImageView) getChildAt(i);
+ imageView.setImageDrawable(icons.icons.get(i));
+ }
+ if (icons.extraItems > 0) {
+ TextView textView = (TextView) checkNotNull(getLastChild());
+ textView.setText(getContext().getString(R.string.zen_mode_plus_n_items,
+ icons.extraItems));
+ }
+
+ applyEnabledDisabledAppearance(isEnabled());
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ applyEnabledDisabledAppearance(isEnabled());
+ }
+
+ private void applyEnabledDisabledAppearance(boolean enabled) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ child.setAlpha(enabled ? 1.0f : DISABLED_ITEM_ALPHA);
+ }
+ }
+
+ private int getChildCount(Class<? extends View> childClass) {
+ int count = 0;
+ for (int i = 0; i < getChildCount(); i++) {
+ if (childClass.isInstance(getChildAt(i))) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ @Nullable
+ private View getLastChild() {
+ if (getChildCount() == 0) {
+ return null;
+ }
+ return getChildAt(getChildCount() - 1);
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ @Nullable
+ Icons getDisplayedIcons() {
+ return mDisplayedIcons;
+ }
+}
diff --git a/src/com/android/settings/notification/modes/IconUtil.java b/src/com/android/settings/notification/modes/IconUtil.java
index dc4d875..33d0d96 100644
--- a/src/com/android/settings/notification/modes/IconUtil.java
+++ b/src/com/android/settings/notification/modes/IconUtil.java
@@ -79,6 +79,7 @@
@Px int innerSizePx = res.getDimensionPixelSize(R.dimen.zen_mode_header_inner_icon_size);
Drawable base = composeIcons(
+ context.getResources(),
background,
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer),
@@ -89,6 +90,7 @@
innerSizePx);
Drawable selected = composeIcons(
+ context.getResources(),
background,
Utils.getColorAttr(context, com.android.internal.R.attr.materialColorPrimary),
outerSizePx,
@@ -111,6 +113,7 @@
*/
static Drawable makeIconPickerHeader(@NonNull Context context, Drawable icon) {
return composeIconCircle(
+ context.getResources(),
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer),
context.getResources().getDimensionPixelSize(
@@ -129,6 +132,7 @@
*/
static Drawable makeIconPickerItem(@NonNull Context context, @DrawableRes int iconResId) {
return composeIconCircle(
+ context.getResources(),
context.getColorStateList(R.color.modes_icon_selectable_background),
context.getResources().getDimensionPixelSize(
R.dimen.zen_mode_icon_list_item_circle_diameter),
@@ -146,6 +150,7 @@
static Drawable makeCircularIconPreferenceItem(@NonNull Context context,
@DrawableRes int iconResId) {
return composeIconCircle(
+ context.getResources(),
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer),
context.getResources().getDimensionPixelSize(
@@ -166,6 +171,7 @@
Resources res = context.getResources();
if (Strings.isNullOrEmpty(displayName)) {
return composeIconCircle(
+ context.getResources(),
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorTertiaryContainer),
res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter),
@@ -204,17 +210,17 @@
return new BitmapDrawable(context.getResources(), bitmap);
}
- private static Drawable composeIconCircle(ColorStateList circleColor, @Px int circleDiameterPx,
- Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
- return composeIcons(new ShapeDrawable(new OvalShape()), circleColor, circleDiameterPx, icon,
- iconColor, iconSizePx);
+ private static Drawable composeIconCircle(Resources res, ColorStateList circleColor,
+ @Px int circleDiameterPx, Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
+ return composeIcons(res, new ShapeDrawable(new OvalShape()), circleColor, circleDiameterPx,
+ icon, iconColor, iconSizePx);
}
- private static Drawable composeIcons(Drawable outer, ColorStateList outerColor,
+ private static Drawable composeIcons(Resources res, Drawable outer, ColorStateList outerColor,
@Px int outerSizePx, Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
- Drawable background = checkNotNull(outer.getConstantState()).newDrawable().mutate();
+ Drawable background = checkNotNull(outer.getConstantState()).newDrawable(res).mutate();
background.setTintList(outerColor);
- Drawable foreground = checkNotNull(icon.getConstantState()).newDrawable().mutate();
+ Drawable foreground = checkNotNull(icon.getConstantState()).newDrawable(res).mutate();
foreground.setTintList(iconColor);
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, foreground });
diff --git a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
index 830baaf..8408624 100644
--- a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
+++ b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
@@ -21,9 +21,13 @@
import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
import static android.app.AutomaticZenRule.TYPE_MANAGED;
import static android.app.AutomaticZenRule.TYPE_OTHER;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.app.AutomaticZenRule.TYPE_THEATER;
+import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -39,6 +43,7 @@
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentActivity;
@@ -124,6 +129,11 @@
title.setText(mode.getName());
}
+ TextView subtitle = findViewById(R.id.mode_name_subtitle);
+ if (subtitle != null) {
+ subtitle.setText(getSubtitle(mode));
+ }
+
ImageView img = findViewById(R.id.image);
if (img != null) {
setImage(img, mode);
@@ -135,6 +145,27 @@
}
}
+ @StringRes
+ @SuppressLint("SwitchIntDef")
+ private static int getSubtitle(ZenMode mode) {
+ if (mode.isSystemOwned()) {
+ return switch (mode.getType()) {
+ case TYPE_SCHEDULE_TIME -> R.string.zen_mode_inspiration_schedule_time;
+ case TYPE_SCHEDULE_CALENDAR -> R.string.zen_mode_inspiration_schedule_calendar;
+ default -> R.string.zen_mode_inspiration_generic; // Custom Manual
+ };
+ } else {
+ return switch (mode.getType()) {
+ case TYPE_BEDTIME -> R.string.zen_mode_inspiration_bedtime;
+ case TYPE_DRIVING -> R.string.zen_mode_inspiration_driving;
+ case TYPE_IMMERSIVE -> R.string.zen_mode_inspiration_immersive;
+ case TYPE_THEATER -> R.string.zen_mode_inspiration_theater;
+ case TYPE_MANAGED -> R.string.zen_mode_inspiration_managed;
+ default -> R.string.zen_mode_inspiration_generic; // Including OTHER, UNKNOWN.
+ };
+ }
+ }
+
private void setImage(@NonNull ImageView img, @NonNull ZenMode mode) {
int drawableRes = switch (mode.getType()) {
case TYPE_BEDTIME -> R.drawable.modes_interstitial_bedtime;
@@ -142,7 +173,9 @@
case TYPE_IMMERSIVE -> R.drawable.modes_interstitial_immersive;
case TYPE_THEATER -> R.drawable.modes_interstitial_theater;
case TYPE_MANAGED -> R.drawable.modes_interstitial_managed;
- case TYPE_OTHER -> R.drawable.modes_interstitial_other;
+ case TYPE_OTHER, TYPE_SCHEDULE_CALENDAR, TYPE_SCHEDULE_TIME ->
+ R.drawable.modes_interstitial_other;
+ case TYPE_UNKNOWN -> R.drawable.modes_interstitial_unknown;
default -> R.drawable.modes_interstitial_unknown;
};
diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
index 45287ab..7b17f0c 100644
--- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
@@ -109,7 +109,7 @@
if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
mPreference.setSummary(R.string.zen_mode_apps_none_apps);
- mPreference.displayIcons(CircularIconSet.EMPTY);
+ mPreference.setIcons(CircularIconSet.EMPTY);
if (mAppSession != null) {
mAppSession.deactivateSession();
}
@@ -151,7 +151,7 @@
ImmutableList<AppEntry> apps = getAppsBypassingDndSortedByName(allApps);
mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, apps));
- mPreference.displayIcons(new CircularIconSet<>(apps,
+ mPreference.setIcons(new CircularIconSet<>(apps,
app -> mAppIconRetriever.apply(app.info)),
APP_ENTRY_EQUIVALENCE);
}
diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java
index 3777299..13aabd3 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -110,9 +110,10 @@
if (mode == null || mode.getStatus() != DISABLED_BY_OTHER) {
return false;
}
+
+ mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode));
// don't come back here from the interstitial
finish();
- mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode));
return true;
}
diff --git a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
index 5b26364..9613d98 100644
--- a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
@@ -72,7 +72,7 @@
preference.setEnabled(zenMode.isEnabled());
preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode));
- ((CircularIconsPreference) preference).displayIcons(getSoundIcons(zenMode.getPolicy()));
+ ((CircularIconsPreference) preference).setIcons(getSoundIcons(zenMode.getPolicy()));
}
private CircularIconSet<Integer> getSoundIcons(ZenPolicy policy) {
diff --git a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
index 9aad460..bf55471 100644
--- a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
@@ -96,7 +96,7 @@
preference.setEnabled(zenMode.isEnabled());
preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy()));
- ((CircularIconsPreference) preference).displayIcons(getPeopleIcons(zenMode.getPolicy()),
+ ((CircularIconsPreference) preference).setIcons(getPeopleIcons(zenMode.getPolicy()),
PEOPLE_ITEM_EQUIVALENCE);
}
diff --git a/src/com/android/settings/notification/zen/ZenAccessSettings.java b/src/com/android/settings/notification/zen/ZenAccessSettings.java
index f765d6d..4b598db 100644
--- a/src/com/android/settings/notification/zen/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/zen/ZenAccessSettings.java
@@ -16,6 +16,7 @@
package com.android.settings.notification.zen;
+import android.app.Flags;
import android.app.NotificationManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -68,6 +69,9 @@
mContext = getActivity();
mPkgMan = mContext.getPackageManager();
mNoMan = mContext.getSystemService(NotificationManager.class);
+ requireActivity().setTitle(Flags.modesApi() && Flags.modesUi()
+ ? R.string.manage_zen_modes_access_title
+ : R.string.manage_zen_access_title);
getSettingsLifecycle().addObserver(
new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
}
@@ -75,7 +79,9 @@
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- setEmptyText(R.string.zen_access_empty_text);
+ setEmptyText(Flags.modesApi() && Flags.modesUi()
+ ? R.string.zen_modes_access_empty_text
+ : R.string.zen_access_empty_text);
}
@Override
@@ -139,7 +145,9 @@
pref.setOnPreferenceClickListener(preference -> {
AppInfoBase.startAppInfoFragment(
ZenAccessDetails.class /* fragment */,
- getString(R.string.manage_zen_access_title) /* titleRes */,
+ getString(Flags.modesApi() && Flags.modesUi()
+ ? R.string.manage_zen_modes_access_title
+ : R.string.manage_zen_access_title),
pkg,
app.uid,
this /* source */,
@@ -154,7 +162,7 @@
/**
* @return the summary for the current state of whether the app associated with the given
- * {@param packageName} is allowed to enter picture-in-picture.
+ * {@param packageName} is allowed to manage DND / Priority Modes.
*/
private int getPreferenceSummary(String packageName) {
final boolean enabled = ZenAccessController.hasAccess(getContext(), packageName);
diff --git a/src/com/android/settings/spa/search/SpaSearchLandingActivity.kt b/src/com/android/settings/spa/search/SpaSearchLandingActivity.kt
index cb5f745..2c0955b 100644
--- a/src/com/android/settings/spa/search/SpaSearchLandingActivity.kt
+++ b/src/com/android/settings/spa/search/SpaSearchLandingActivity.kt
@@ -27,9 +27,6 @@
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settings.password.PasswordUtils
import com.android.settings.spa.SpaDestination
-import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
-import com.google.protobuf.ByteString
-import com.google.protobuf.InvalidProtocolBufferException
class SpaSearchLandingActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -48,22 +45,17 @@
companion object {
@VisibleForTesting
fun tryLaunch(context: Context, keyString: String) {
- val key =
- try {
- SpaSearchLandingKey.parseFrom(ByteString.copyFromUtf8(keyString))
- } catch (e: InvalidProtocolBufferException) {
- Log.w(TAG, "arg key ($keyString) invalid", e)
- return
- }
-
+ val key = decodeToSpaSearchLandingKey(keyString) ?: return
if (key.hasSpaPage()) {
val destination = key.spaPage.destination
if (destination.isNotEmpty()) {
+ Log.d(TAG, "Launch SPA search result: ${key.spaPage}")
SpaDestination(destination = destination, highlightMenuKey = null)
.startFromExportedActivity(context)
}
}
if (key.hasFragment()) {
+ Log.d(TAG, "Launch fragment search result: ${key.fragment}")
val arguments =
Bundle().apply {
key.fragment.argumentsMap.forEach { (k, v) ->
diff --git a/src/com/android/settings/spa/search/SpaSearchLandingKeyExt.kt b/src/com/android/settings/spa/search/SpaSearchLandingKeyExt.kt
new file mode 100644
index 0000000..9540d9b
--- /dev/null
+++ b/src/com/android/settings/spa/search/SpaSearchLandingKeyExt.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.spa.search
+
+import android.util.Base64
+import android.util.Log
+import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
+
+private const val TAG = "SpaSearchLandingKeyExt"
+
+fun SpaSearchLandingKey.encodeToString(): String =
+ Base64.encodeToString(toByteArray(), Base64.DEFAULT)
+
+fun decodeToSpaSearchLandingKey(input: String): SpaSearchLandingKey? =
+ try {
+ SpaSearchLandingKey.parseFrom(Base64.decode(input, Base64.DEFAULT))
+ } catch (e: Exception) {
+ Log.w(TAG, "SpaSearchLandingKey ($input) invalid", e)
+ null
+ }
diff --git a/src/com/android/settings/spa/search/SpaSearchRepository.kt b/src/com/android/settings/spa/search/SpaSearchRepository.kt
index e5334dd..b1003ae 100644
--- a/src/com/android/settings/spa/search/SpaSearchRepository.kt
+++ b/src/com/android/settings/spa/search/SpaSearchRepository.kt
@@ -96,7 +96,7 @@
keywords: String? = null,
) =
SearchIndexableRaw(context).apply {
- key = spaSearchLandingKey.toByteString().toStringUtf8()
+ key = spaSearchLandingKey.encodeToString()
title = itemTitle
this.keywords = keywords
intentAction = SEARCH_LANDING_ACTION
diff --git a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
index e523831..4dc2e9e 100644
--- a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
+++ b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
@@ -397,7 +397,7 @@
* @return {@code true} if Wi-Fi Hotspot 5 GHz Band is available
*/
public boolean is5gAvailable() {
- if (!mBand5g.isUsableChannelsReady && is5GHzBandSupported()) {
+ if (!mBand5g.isChannelsReady && is5GHzBandSupported()) {
isChannelAvailable(mBand5g);
}
return mBand5g.isAvailable();
@@ -439,7 +439,7 @@
* @return {@code true} if Wi-Fi Hotspot 6 GHz Band is available
*/
public boolean is6gAvailable() {
- if (!mBand6g.isUsableChannelsReady && is6GHzBandSupported()) {
+ if (!mBand6g.isChannelsReady && is6GHzBandSupported()) {
isChannelAvailable(mBand6g);
}
return mBand6g.isAvailable();
@@ -475,19 +475,19 @@
List<WifiAvailableChannel> channels =
mWifiManager.getAllowedChannels(sapBand.band, OP_MODE_SAP);
log("isChannelAvailable(), band:" + sapBand.band + ", channels:" + channels);
- sapBand.hasUsableChannels = (channels != null && channels.size() > 0);
- sapBand.isUsableChannelsUnsupported = false;
+ sapBand.hasChannels = (channels != null && channels.size() > 0);
+ sapBand.isChannelsUnsupported = false;
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Querying usable SAP channels failed, band:" + sapBand.band);
- sapBand.hasUsableChannels = false;
- sapBand.isUsableChannelsUnsupported = true;
+ Log.e(TAG, "Querying SAP channels failed, band:" + sapBand.band);
+ sapBand.hasChannels = false;
+ sapBand.isChannelsUnsupported = true;
} catch (UnsupportedOperationException e) {
// This is expected on some hardware.
- Log.e(TAG, "Querying usable SAP channels is unsupported, band:" + sapBand.band);
- sapBand.hasUsableChannels = false;
- sapBand.isUsableChannelsUnsupported = true;
+ Log.e(TAG, "Querying SAP channels is unsupported, band:" + sapBand.band);
+ sapBand.hasChannels = false;
+ sapBand.isChannelsUnsupported = true;
}
- sapBand.isUsableChannelsReady = true;
+ sapBand.isChannelsReady = true;
log("isChannelAvailable(), " + sapBand);
return sapBand.isAvailable();
}
@@ -531,8 +531,8 @@
}
protected void purgeRefreshData() {
- mBand5g.isUsableChannelsReady = false;
- mBand6g.isUsableChannelsReady = false;
+ mBand5g.isChannelsReady = false;
+ mBand6g.isChannelsReady = false;
}
protected void startAutoRefresh() {
@@ -615,15 +615,15 @@
@VisibleForTesting
void updateCapabilityChanged() {
- if (mBand5g.isUsableChannelsUnsupported) {
+ if (mBand5g.isChannelsUnsupported) {
update5gAvailable();
log("updateCapabilityChanged(), " + mBand5g);
}
- if (mBand6g.isUsableChannelsUnsupported) {
+ if (mBand6g.isChannelsUnsupported) {
update6gAvailable();
log("updateCapabilityChanged(), " + mBand6g);
}
- if (mBand5g.isUsableChannelsUnsupported || mBand6g.isUsableChannelsUnsupported) {
+ if (mBand5g.isChannelsUnsupported || mBand6g.isChannelsUnsupported) {
updateSpeedType();
}
}
@@ -676,9 +676,9 @@
@VisibleForTesting
static class SapBand {
public int band;
- public boolean isUsableChannelsReady;
- public boolean hasUsableChannels;
- public boolean isUsableChannelsUnsupported;
+ public boolean isChannelsReady;
+ public boolean hasChannels;
+ public boolean isChannelsUnsupported;
public boolean hasCapability;
SapBand(int band) {
@@ -689,7 +689,7 @@
* Return whether SoftAp band is available or not.
*/
public boolean isAvailable() {
- return isUsableChannelsUnsupported ? hasCapability : hasUsableChannels;
+ return isChannelsUnsupported ? hasCapability : hasChannels;
}
@Override
@@ -697,10 +697,10 @@
public String toString() {
return "SapBand{"
+ "band:" + band
- + ",isUsableChannelsReady:" + isUsableChannelsReady
- + ",hasUsableChannels:" + hasUsableChannels
- + ",isUsableChannelsUnsupported:" + isUsableChannelsUnsupported
- + ",hasChannelsCapability:" + hasCapability
+ + ",isChannelsReady:" + isChannelsReady
+ + ",hasChannels:" + hasChannels
+ + ",isChannelsUnsupported:" + isChannelsUnsupported
+ + ",hasCapability:" + hasCapability
+ '}';
}
}
diff --git a/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java
index 98c774e..826bae9 100644
--- a/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java
@@ -22,22 +22,35 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.provider.Settings;
+import android.hardware.input.InputSettings;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
+import com.android.hardware.input.Flags;
+import com.android.settings.testutils.shadow.ShadowSystemSettings;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowSystemSettings.class,
+})
public class TouchpadVisualizerPreferenceControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private PreferenceScreen mScreen;
@Mock
@@ -57,55 +70,52 @@
}
@Test
+ @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
public void updateState_touchpadVisualizerEnabled_shouldCheckedPreference() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.TOUCHPAD_VISUALIZER, ShowTapsPreferenceController.SETTING_VALUE_ON);
-
+ InputSettings.setTouchpadVisualizer(mContext, true);
mController.updateState(mPreference);
verify(mPreference).setChecked(true);
}
@Test
+ @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
public void updateState_touchpadVisualizerDisabled_shouldUncheckedPreference() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.TOUCHPAD_VISUALIZER,
- ShowTapsPreferenceController.SETTING_VALUE_OFF);
-
+ InputSettings.setTouchpadVisualizer(mContext, false);
mController.updateState(mPreference);
verify(mPreference).setChecked(false);
}
@Test
+ @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
public void onPreferenceChange_preferenceChecked_shouldEnableTouchpadVisualizer() {
mController.onPreferenceChange(mPreference, true /* new value */);
- final int touchpadVisualizer = Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+ final boolean touchpadVisualizer = InputSettings.useTouchpadVisualizer(mContext);
- assertThat(touchpadVisualizer).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_ON);
+ assertThat(touchpadVisualizer).isTrue();
}
@Test
+ @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
public void onPreferenceChange_preferenceUnchecked_shouldDisableTouchpadVisualizer() {
mController.onPreferenceChange(mPreference, false /* new value */);
- final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+ final boolean touchpadVisualizer = InputSettings.useTouchpadVisualizer(mContext);
- assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+ assertThat(touchpadVisualizer).isFalse();
}
@Test
+ @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
public void onDeveloperOptionsSwitchDisabled_preferenceShouldBeEnabled() {
mController.onDeveloperOptionsSwitchDisabled();
- final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+ final boolean touchpadVisualizer = InputSettings.useTouchpadVisualizer(mContext);
- assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+ assertThat(touchpadVisualizer).isFalse();
verify(mPreference).setEnabled(false);
verify(mPreference).setChecked(false);
}
-}
\ No newline at end of file
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/CircularIconsPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/modes/CircularIconsPreferenceTest.java
index d145f25..5544832 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/CircularIconsPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/CircularIconsPreferenceTest.java
@@ -62,8 +62,7 @@
private Context mContext;
private CircularIconsPreference mPreference;
- private PreferenceViewHolder mViewHolder;
- private ViewGroup mContainer;
+ private CircularIconsView mContainer;
private int mOneIconWidth;
@@ -73,179 +72,211 @@
mContext = RuntimeEnvironment.application;
CircularIconSet.sExecutorService = MoreExecutors.newDirectExecutorService();
mPreference = new TestableCircularIconsPreference(mContext);
- // Tests should call bindAndMeasureViewHolder() so that icons can be added.
+ // Tests should call bindAndLayoutViewHolder() so that icons can be added.
Resources res = mContext.getResources();
mOneIconWidth = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
+ res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
}
- private void bindAndMeasureViewHolder(int viewWidth) {
+ private void bindAndLayoutViewHolder(int viewWidth) {
bindViewHolder();
- measureViewHolder(viewWidth);
+ layoutViewHolder(viewWidth);
}
private void bindViewHolder() {
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
null);
mContainer = checkNotNull(preferenceView.findViewById(R.id.circles_container));
- mViewHolder = PreferenceViewHolder.createInstanceForTests(preferenceView);
- mPreference.onBindViewHolder(mViewHolder);
+ mContainer.setUiExecutor(MoreExecutors.directExecutor());
+ PreferenceViewHolder viewHolder = PreferenceViewHolder.createInstanceForTests(
+ preferenceView);
+ mPreference.onBindViewHolder(viewHolder);
}
- private void measureViewHolder(int viewWidth) {
+ private void layoutViewHolder(int viewWidth) {
checkState(mContainer != null, "Call bindViewHolder() first!");
mContainer.measure(makeMeasureSpec(viewWidth, View.MeasureSpec.EXACTLY),
makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
- mContainer.getViewTreeObserver().dispatchOnGlobalLayout();
+ mContainer.layout(0, 0, viewWidth, 1000);
}
@Test
- public void displayIcons_loadsIcons() {
+ public void setIcons_loadsIcons() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new);
- bindAndMeasureViewHolder(VIEW_WIDTH);
- mPreference.displayIcons(iconSet);
+ bindAndLayoutViewHolder(VIEW_WIDTH);
+ mPreference.setIcons(iconSet);
- assertThat(getIcons(mContainer)).hasSize(2);
- assertThat(((ColorDrawable) getIcons(mContainer).get(0)).getColor()).isEqualTo(1);
- assertThat(((ColorDrawable) getIcons(mContainer).get(1)).getColor()).isEqualTo(2);
+ assertThat(getDrawables(mContainer)).hasSize(2);
+ assertThat(((ColorDrawable) getDrawables(mContainer).get(0)).getColor()).isEqualTo(1);
+ assertThat(((ColorDrawable) getDrawables(mContainer).get(1)).getColor()).isEqualTo(2);
assertThat(getPlusText(mContainer)).isNull();
}
@Test
- public void displayIcons_noIcons_hidesRow() {
+ public void setIcons_noIcons_hidesRow() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(),
ColorDrawable::new);
- bindAndMeasureViewHolder(VIEW_WIDTH);
- mPreference.displayIcons(iconSet);
+ bindAndLayoutViewHolder(VIEW_WIDTH);
+ mPreference.setIcons(iconSet);
assertThat(mContainer.getVisibility()).isEqualTo(View.GONE);
}
@Test
- public void displayIcons_exactlyMaxIcons_loadsAllIcons() throws Exception {
+ public void setIcons_exactlyMaxIcons_loadsAllIcons() throws Exception {
int width = 300;
int fittingCircles = width / mOneIconWidth;
CircularIconSet<Integer> iconSet = new CircularIconSet<>(
IntStream.range(0, fittingCircles).boxed().toList(),
ColorDrawable::new);
- bindAndMeasureViewHolder(width);
- mPreference.displayIcons(iconSet);
+ bindAndLayoutViewHolder(width);
+ mPreference.setIcons(iconSet);
- assertThat(getIcons(mContainer)).hasSize(fittingCircles);
- assertThat(getIcons(mContainer)).containsExactlyElementsIn(
+ assertThat(getDrawables(mContainer)).hasSize(fittingCircles);
+ assertThat(getDrawables(mContainer)).containsExactlyElementsIn(
Futures.allAsList(iconSet.getIcons()).get()).inOrder();
assertThat(getPlusText(mContainer)).isNull();
-
}
@Test
- public void displayIcons_tooManyIcons_loadsFirstNAndPlusIcon() throws Exception {
+ public void setIcons_tooManyIcons_loadsFirstNAndPlusIcon() throws Exception {
int width = 300;
int fittingCircles = width / mOneIconWidth;
CircularIconSet<Integer> iconSet = new CircularIconSet<>(
IntStream.range(0, fittingCircles + 5).boxed().toList(),
ColorDrawable::new);
- bindAndMeasureViewHolder(width);
- mPreference.displayIcons(iconSet);
+ bindAndLayoutViewHolder(width);
+ mPreference.setIcons(iconSet);
// N-1 icons, plus (+6) text.
- assertThat(getIcons(mContainer)).hasSize(fittingCircles - 1);
- assertThat(getIcons(mContainer)).containsExactlyElementsIn(
+ assertThat(getDrawables(mContainer)).hasSize(fittingCircles - 1);
+ assertThat(getDrawables(mContainer)).containsExactlyElementsIn(
Futures.allAsList(iconSet.getIcons(fittingCircles - 1)).get())
.inOrder();
assertThat(getPlusText(mContainer)).isEqualTo("+6");
}
@Test
- public void displayIcons_teenyTinySpace_showsPlusIcon_noCrash() {
+ public void setIcons_teenyTinySpace_showsPlusIcon_noCrash() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new);
- bindAndMeasureViewHolder(1);
- mPreference.displayIcons(iconSet);
+ bindAndLayoutViewHolder(1);
+ mPreference.setIcons(iconSet);
- assertThat(getIcons(mContainer)).isEmpty();
+ assertThat(getDrawables(mContainer)).isEmpty();
assertThat(getPlusText(mContainer)).isEqualTo("+2");
}
@Test
- public void displayIcons_beforeBind_loadsIconsOnBindAndMeasure() {
+ public void setIcons_beforeBind_loadsIconsOnBindAndMeasure() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new);
- mPreference.displayIcons(iconSet);
- assertThat(mPreference.getLoadedIcons()).isNull(); // Hold...
+ mPreference.setIcons(iconSet);
+ assertThat(mContainer).isNull(); // Hold...
bindViewHolder();
- assertThat(mPreference.getLoadedIcons()).isNull(); // Hooooold...
+ assertThat(getDrawables(mContainer)).hasSize(0); // Hooooold...
- measureViewHolder(VIEW_WIDTH);
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(3);
- assertThat(getIcons(mContainer)).hasSize(3);
+ layoutViewHolder(VIEW_WIDTH);
+ assertThat(getDrawables(mContainer)).hasSize(3);
}
@Test
- public void displayIcons_beforeMeasure_loadsIconsOnMeasure() {
+ public void setIcons_beforeMeasure_loadsIconsOnMeasure() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new);
bindViewHolder();
- mPreference.displayIcons(iconSet);
- assertThat(mPreference.getLoadedIcons()).isNull();
+ mPreference.setIcons(iconSet);
+ assertThat(getDrawables(mContainer)).hasSize(0);
- measureViewHolder(VIEW_WIDTH);
- assertThat(getIcons(mContainer)).hasSize(3);
+ layoutViewHolder(VIEW_WIDTH);
+ assertThat(getDrawables(mContainer)).hasSize(3);
}
@Test
- public void displayIcons_calledAgain_reloadsIcons() {
+ public void setIcons_calledAgain_reloadsIcons() {
CircularIconSet<Integer> threeIcons = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new);
CircularIconSet<Integer> twoIcons = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new);
CircularIconSet<Integer> fourIcons = new CircularIconSet<>(ImmutableList.of(1, 2, 3, 4),
ColorDrawable::new);
- bindAndMeasureViewHolder(VIEW_WIDTH);
+ bindAndLayoutViewHolder(VIEW_WIDTH);
- mPreference.displayIcons(threeIcons);
- assertThat(mPreference.getLoadedIcons()).isNotNull();
- assertThat(getIcons(mContainer)).hasSize(3);
+ mPreference.setIcons(threeIcons);
+ assertThat(getDrawables(mContainer)).hasSize(3);
- mPreference.displayIcons(twoIcons);
- assertThat(mPreference.getLoadedIcons()).isNotNull();
- assertThat(getIcons(mContainer)).hasSize(2);
+ mPreference.setIcons(twoIcons);
+ assertThat(getDrawables(mContainer)).hasSize(2);
- mPreference.displayIcons(fourIcons);
- assertThat(mPreference.getLoadedIcons()).isNotNull();
- assertThat(getIcons(mContainer)).hasSize(4);
+ mPreference.setIcons(fourIcons);
+ assertThat(getDrawables(mContainer)).hasSize(4);
}
@Test
- public void displayIcons_sameSet_doesNotReloadIcons() {
+ public void setIcons_sameSet_doesNotReloadIcons() {
CircularIconSet<Integer> one = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new);
CircularIconSet<Integer> same = Mockito.spy(new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new));
when(same.getIcons()).thenThrow(new RuntimeException("Shouldn't be called!"));
- bindAndMeasureViewHolder(VIEW_WIDTH);
+ bindAndLayoutViewHolder(VIEW_WIDTH);
- mPreference.displayIcons(one);
+ mPreference.setIcons(one);
- mPreference.displayIcons(same); // if no exception, wasn't called.
+ mPreference.setIcons(same); // if no exception, wasn't called.
}
@Test
+ public void sizeChanged_reloadsIconsIfDifferentFit() {
+ CircularIconSet<Integer> largeIconSet = new CircularIconSet<>(
+ IntStream.range(0, 100).boxed().toList(),
+ ColorDrawable::new);
+ mPreference.setIcons(largeIconSet);
+
+ // Base space -> some icons
+ int firstWidth = 600;
+ int firstFittingCircles = firstWidth / mOneIconWidth;
+ bindAndLayoutViewHolder(firstWidth);
+
+ assertThat(getDrawables(mContainer)).hasSize(firstFittingCircles - 1);
+ assertThat(getPlusText(mContainer)).isEqualTo("+" + (100 - (firstFittingCircles - 1)));
+
+ // More space -> more icons
+ int secondWidth = 1000;
+ int secondFittingCircles = secondWidth / mOneIconWidth;
+ assertThat(secondFittingCircles).isGreaterThan(firstFittingCircles);
+ bindAndLayoutViewHolder(secondWidth);
+
+ assertThat(getDrawables(mContainer)).hasSize(secondFittingCircles - 1);
+ assertThat(getPlusText(mContainer)).isEqualTo("+" + (100 - (secondFittingCircles - 1)));
+
+ // Less space -> fewer icons
+ int thirdWidth = 600;
+ int thirdFittingCircles = thirdWidth / mOneIconWidth;
+ bindAndLayoutViewHolder(thirdWidth);
+
+ assertThat(getDrawables(mContainer)).hasSize(thirdFittingCircles - 1);
+ assertThat(getPlusText(mContainer)).isEqualTo("+" + (100 - (thirdFittingCircles - 1)));
+ }
+
+
+ @Test
public void onBindViewHolder_withDifferentView_reloadsIconsCorrectly() {
View preferenceViewOne = LayoutInflater.from(mContext).inflate(
mPreference.getLayoutResource(), null);
- ViewGroup containerOne = preferenceViewOne.findViewById(R.id.circles_container);
+ CircularIconsView containerOne = preferenceViewOne.findViewById(R.id.circles_container);
+ containerOne.setUiExecutor(MoreExecutors.directExecutor());
PreferenceViewHolder viewHolderOne = PreferenceViewHolder.createInstanceForTests(
preferenceViewOne);
containerOne.measure(makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
@@ -253,7 +284,8 @@
View preferenceViewTwo = LayoutInflater.from(mContext).inflate(
mPreference.getLayoutResource(), null);
- ViewGroup containerTwo = preferenceViewTwo.findViewById(R.id.circles_container);
+ CircularIconsView containerTwo = preferenceViewTwo.findViewById(R.id.circles_container);
+ containerTwo.setUiExecutor(MoreExecutors.directExecutor());
PreferenceViewHolder viewHolderTwo = PreferenceViewHolder.createInstanceForTests(
preferenceViewTwo);
containerTwo.measure(makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
@@ -265,25 +297,25 @@
ColorDrawable::new);
mPreference.onBindViewHolder(viewHolderOne);
- mPreference.displayIcons(iconSetOne);
- assertThat(getIcons(containerOne)).hasSize(3);
+ mPreference.setIcons(iconSetOne);
+ assertThat(getDrawables(containerOne)).hasSize(3);
mPreference.onBindViewHolder(viewHolderTwo);
- assertThat(getIcons(containerTwo)).hasSize(3);
+ assertThat(getDrawables(containerTwo)).hasSize(3);
- mPreference.displayIcons(iconSetTwo);
+ mPreference.setIcons(iconSetTwo);
// The second view is updated and the first view is unaffected.
- assertThat(getIcons(containerTwo)).hasSize(2);
- assertThat(getIcons(containerOne)).hasSize(3);
+ assertThat(getDrawables(containerTwo)).hasSize(2);
+ assertThat(getDrawables(containerOne)).hasSize(3);
}
@Test
- public void setEnabled_afterDisplayIcons_showsEnabledOrDisabledImages() {
+ public void setEnabled_afterSetIcons_showsEnabledOrDisabledImages() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new);
- bindAndMeasureViewHolder(VIEW_WIDTH);
- mPreference.displayIcons(iconSet);
+ bindAndLayoutViewHolder(VIEW_WIDTH);
+ mPreference.setIcons(iconSet);
assertThat(getViews(mContainer)).hasSize(2);
mPreference.setEnabled(false);
@@ -294,13 +326,13 @@
}
@Test
- public void setEnabled_beforeDisplayIcons_showsEnabledOrDisabledImages() {
+ public void setEnabled_beforeSetIcons_showsEnabledOrDisabledImages() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new);
mPreference.setEnabled(false);
- bindAndMeasureViewHolder(VIEW_WIDTH);
- mPreference.displayIcons(iconSet);
+ bindAndLayoutViewHolder(VIEW_WIDTH);
+ mPreference.setIcons(iconSet);
assertThat(getViews(mContainer)).hasSize(2);
assertThat(getViews(mContainer).get(0).getAlpha()).isLessThan(1f);
@@ -314,7 +346,7 @@
return views;
}
- private static List<Drawable> getIcons(ViewGroup container) {
+ private static List<Drawable> getDrawables(ViewGroup container) {
ArrayList<Drawable> drawables = new ArrayList<>();
for (int i = 0; i < container.getChildCount(); i++) {
if (container.getChildAt(i) instanceof ImageView imageView) {
diff --git a/tests/robotests/src/com/android/settings/notification/modes/TestableCircularIconsPreference.java b/tests/robotests/src/com/android/settings/notification/modes/TestableCircularIconsPreference.java
index 6fefcac..6c1b059 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/TestableCircularIconsPreference.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/TestableCircularIconsPreference.java
@@ -20,14 +20,12 @@
import androidx.preference.PreferenceViewHolder;
-import com.google.common.util.concurrent.MoreExecutors;
-
class TestableCircularIconsPreference extends CircularIconsPreference {
private PreferenceViewHolder mLastViewHolder;
TestableCircularIconsPreference(Context context) {
- super(context, MoreExecutors.directExecutor());
+ super(context);
}
@Override
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
index 29e9cf9..4148fa3 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
@@ -19,6 +19,7 @@
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -52,6 +53,7 @@
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceViewHolder;
+import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -82,6 +84,7 @@
private ZenModeAppsLinkPreferenceController mController;
private CircularIconsPreference mPreference;
+ private CircularIconsView mIconsView;
private Context mContext;
@Mock
@@ -114,6 +117,8 @@
// Ensure the preference view is bound & measured (needed to add child ImageViews).
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
null);
+ mIconsView = checkNotNull(preferenceView.findViewById(R.id.circles_container));
+ mIconsView.setUiExecutor(MoreExecutors.directExecutor());
preferenceView.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView);
@@ -273,7 +278,7 @@
appEntries.add(createAppEntry("test2", mContext.getUserId()));
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(2);
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(2);
}
@Test
@@ -313,7 +318,7 @@
mController.updateState(mPreference, zenModeWithNone);
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
verifyNoMoreInteractions(mApplicationsState);
verifyNoMoreInteractions(mSession);
@@ -322,7 +327,7 @@
verify(mApplicationsState).newSession(any(), any());
verify(mSession).rebuild(any(), any(), anyBoolean());
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(1);
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
}
@Test
@@ -343,11 +348,11 @@
verify(mApplicationsState).newSession(any(), any());
verify(mSession).rebuild(any(), any(), anyBoolean());
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(1);
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
mController.updateState(mPreference, zenModeWithNone);
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
verify(mSession).deactivateSession();
verifyNoMoreInteractions(mSession);
verifyNoMoreInteractions(mApplicationsState);
@@ -356,7 +361,7 @@
// updateState()) is ignored.
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java
index 8aa87e6..3db70fa 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java
@@ -95,7 +95,7 @@
mController.updateState(pref, mode);
- verify(pref).displayIcons(argThat(iconSet -> iconSet.size() == 3));
+ verify(pref).setIcons(argThat(iconSet -> iconSet.size() == 3));
}
@Test
@@ -107,7 +107,7 @@
mController.updateState(pref, mode);
- verify(pref).displayIcons(argThat(iconSet ->
+ verify(pref).setIcons(argThat(iconSet ->
iconSet.size() == ZenModeSummaryHelper.OTHER_SOUND_CATEGORIES.size()));
}
}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
index a4d141e..8555d71 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
@@ -22,6 +22,7 @@
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -50,6 +51,7 @@
import androidx.preference.PreferenceViewHolder;
+import com.android.settings.R;
import com.android.settings.notification.modes.ZenHelperBackend.Contact;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.settingslib.notification.modes.TestModeBuilder;
@@ -76,6 +78,7 @@
private ZenModePeopleLinkPreferenceController mController;
private CircularIconsPreference mPreference;
+ private CircularIconsView mIconsView;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -94,6 +97,8 @@
// Ensure the preference view is bound & measured (needed to add icons).
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
null);
+ mIconsView = checkNotNull(preferenceView.findViewById(R.id.circles_container));
+ mIconsView.setUiExecutor(MoreExecutors.directExecutor());
preferenceView.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView);
@@ -142,9 +147,9 @@
mController.updateState(mPreference, mode);
- assertThat(mPreference.getLoadedIcons()).isNotNull();
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(2);
- assertThat(mPreference.getLoadedIcons().icons().stream()
+ assertThat(mIconsView.getDisplayedIcons()).isNotNull();
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(2);
+ assertThat(mIconsView.getDisplayedIcons().icons().stream()
.map(ColorDrawable.class::cast)
.map(d -> d.getColor()).toList())
.containsExactly(2, 3).inOrder();
@@ -162,9 +167,9 @@
mController.updateState(mPreference, mode);
- assertThat(mPreference.getLoadedIcons()).isNotNull();
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(4);
- assertThat(mPreference.getLoadedIcons().icons().stream()
+ assertThat(mIconsView.getDisplayedIcons()).isNotNull();
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(4);
+ assertThat(mIconsView.getDisplayedIcons().icons().stream()
.map(ColorDrawable.class::cast)
.map(d -> d.getColor()).toList())
.containsExactly(1, 2, 3, 4).inOrder();
@@ -182,8 +187,8 @@
mController.updateState(mPreference, mode);
- assertThat(mPreference.getLoadedIcons()).isNotNull();
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(1);
+ assertThat(mIconsView.getDisplayedIcons()).isNotNull();
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
verify(mHelperBackend, never()).getContactPhoto(any());
}
@@ -201,8 +206,8 @@
mController.updateState(mPreference, mode);
- assertThat(mPreference.getLoadedIcons()).isNotNull();
- assertThat(mPreference.getLoadedIcons().icons()).hasSize(3);
+ assertThat(mIconsView.getDisplayedIcons()).isNotNull();
+ assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(3);
verify(mConversationIconFactory, times(3)).getConversationDrawable((ShortcutInfo) any(),
any(), anyInt(), anyBoolean());
}
diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModeAddBypassingAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModeAddBypassingAppsPreferenceControllerTest.java
index 27df890..2569ca3 100644
--- a/tests/robotests/src/com/android/settings/notification/zen/ZenModeAddBypassingAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModeAddBypassingAppsPreferenceControllerTest.java
@@ -162,7 +162,4 @@
assertThat(pref.getKey()).isEqualTo(
ZenModeAddBypassingAppsPreferenceController.KEY_NO_APPS);
}
-
- // TODO(b/331624810): Add tests to verify updateAppList() when the filter is
- // ApplicationsState.FILTER_ENABLED_NOT_QUIET
}
diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
index 19433f3..e7fc3ed 100644
--- a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
+++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
@@ -111,9 +111,10 @@
var fingerprintEnrollViewModel =
FingerprintEnrollViewModel(
- fingerprintManagerInteractor,
gatekeeperViewModel,
navigationViewModel,
+ fingerprintManagerInteractor,
+ fingerprintManagerInteractor,
)
var fingerprintEnrollEnrollingViewModel =
diff --git a/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt b/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt
index 52df724..f61a3d3 100644
--- a/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt
+++ b/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt
@@ -22,7 +22,14 @@
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.AuthenitcateInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
+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.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
@@ -35,7 +42,15 @@
import kotlinx.coroutines.flow.flowOf
/** Fake to be used by other classes to easily fake the FingerprintManager implementation. */
-class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
+class FakeFingerprintManagerInteractor :
+ AuthenitcateInteractor,
+ CanEnrollFingerprintsInteractor,
+ EnrolledFingerprintsInteractor,
+ EnrollFingerprintInteractor,
+ GenerateChallengeInteractor,
+ RemoveFingerprintInteractor,
+ RenameFingerprintInteractor,
+ SensorInteractor {
var enrollableFingerprints: Int = 5
var enrolledFingerprintsInternal: MutableList<FingerprintData> = mutableListOf()
@@ -67,19 +82,22 @@
override val enrolledFingerprints: Flow<List<FingerprintData>> = flow {
emit(enrolledFingerprintsInternal)
}
-
override val canEnrollFingerprints: Flow<Boolean> = flow {
emit(enrolledFingerprintsInternal.size < enrollableFingerprints)
}
- override val sensorPropertiesInternal: Flow<FingerprintSensor?> = flow { emit(sensorProp) }
+ override fun maxFingerprintsEnrollable(): Int {
+ return enrollableFingerprints
+ }
- override val maxEnrollableFingerprints: Flow<Int> = flow { emit(enrollableFingerprints) }
+ override val sensorPropertiesInternal: Flow<FingerprintSensor?> = flow { emit(sensorProp) }
+ override val hasSideFps: Flow<Boolean> =
+ flowOf(sensorProp.sensorType == FingerprintSensorType.POWER_BUTTON)
override suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
- fingerprintEnrollOptions: FingerprintEnrollOptions
+ fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState> = flowOf(*enrollStateViewModel.toTypedArray())
override suspend fun removeFingerprint(fp: FingerprintData): Boolean {
@@ -92,7 +110,4 @@
}
}
- override suspend fun hasSideFps(): Boolean {
- return sensorProp.sensorType == FingerprintSensorType.POWER_BUTTON
- }
}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
index 7124b6a..f4974e9 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
@@ -29,7 +29,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.core.BasePreferenceController.AVAILABLE
-import com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE
import com.android.settings.datausage.DataUsageUtils
import com.android.settings.datausage.lib.DataUsageLib
import com.android.settings.datausage.lib.NetworkCycleDataRepository
@@ -77,7 +76,6 @@
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
- .initMocks(this)
.spyStatic(DataUsageUtils::class.java)
.spyStatic(DataUsageLib::class.java)
.strictness(Strictness.LENIENT)
@@ -101,19 +99,11 @@
}
@Test
- fun getAvailabilityStatus_validSubId_returnAvailable() {
+ fun getAvailabilityStatus_returnAvailable() {
assertThat(controller.availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
- fun getAvailabilityStatus_invalidSubId_returnUnsearchable() {
- val availabilityStatus =
- controller.getAvailabilityStatus(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
-
- assertThat(availabilityStatus).isEqualTo(AVAILABLE_UNSEARCHABLE)
- }
-
- @Test
fun handlePreferenceTreeClick_startActivity() = runBlocking {
val usageData = NetworkUsageData(START_TIME, END_TIME, 1L)
repository.stub {
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
index bf51208..ad50433 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
@@ -30,8 +30,8 @@
import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
import com.android.settings.spa.search.SpaSearchLandingActivity
+import com.android.settings.spa.search.decodeToSpaSearchLandingKey
import com.google.common.truth.Truth.assertThat
-import com.google.protobuf.ByteString
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -104,7 +104,7 @@
searchIndexableData.searchIndexProvider.getDynamicRawDataToIndex(context, true)
assertThat(dynamicRawDataToIndex).hasSize(1)
val rawData = dynamicRawDataToIndex[0]
- val key = SpaSearchLandingKey.parseFrom(ByteString.copyFromUtf8(rawData.key))
+ val key = decodeToSpaSearchLandingKey(rawData.key)
assertThat(key)
.isEqualTo(
SpaSearchLandingKey.newBuilder()
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/UiccSlotRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/UiccSlotRepositoryTest.kt
new file mode 100644
index 0000000..96aa151
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/UiccSlotRepositoryTest.kt
@@ -0,0 +1,262 @@
+/*
+ * 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.network.telephony
+
+import android.telephony.TelephonyManager
+import android.telephony.UiccPortInfo
+import android.telephony.UiccSlotInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class UiccSlotRepositoryTest {
+
+ private val mockTelephonyManager = mock<TelephonyManager>()
+
+ private val repository = UiccSlotRepository(mockTelephonyManager)
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_oneSimSlotDeviceActiveEsim_returnsFalse() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfo(
+ isEuicc = true, isRemovable = false, logicalSlotIdx = 1, isActive = true),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_activeRemovableEsimAndInactivePsim_returnsFalse() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfo(
+ isEuicc = true, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = -1, isActive = false),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_activeRemovableEsimAndActivePsim_returnsTrue() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+ createUiccSlotInfo(
+ isEuicc = true, isRemovable = true, logicalSlotIdx = 1, isActive = true),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_inactiveRemovableEsimAndActivePsim_returnsTrue() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfo(
+ isEuicc = true, isRemovable = true, logicalSlotIdx = -1, isActive = false),
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_twoActiveRemovableEsimsAndInactivePsim_returnsFalse() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfoForRemovableEsimMep(
+ logicalSlotIdx1 = 0,
+ isActiveEsim1 = true,
+ logicalSlotIdx2 = 1,
+ isActiveEsim2 = true,
+ ),
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = -1, isActive = false),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_oneActiveOneInactiveRemovableEsimActivePsim_returnsTrue() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfoForRemovableEsimMep(
+ logicalSlotIdx1 = 1,
+ isActiveEsim1 = true,
+ logicalSlotIdx2 = -1,
+ isActiveEsim2 = false,
+ ),
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_activePsim_returnsTrue() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_inactivePsim_returnsFalse() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = -1, isActive = false),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_activeEsimAndActivePsim_returnsTrue() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+ createUiccSlotInfo(
+ isEuicc = true, isRemovable = false, logicalSlotIdx = 1, isActive = true),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_activeEsimAndInactivePsim_returnsFalse() {
+ mockTelephonyManager.stub {
+ on { uiccSlotsInfo } doReturn
+ arrayOf(
+ createUiccSlotInfo(
+ isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = false),
+ createUiccSlotInfo(
+ isEuicc = true, isRemovable = false, logicalSlotIdx = 1, isActive = true),
+ )
+ }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isFalse()
+ }
+
+ @Test
+ fun anyRemovablePhysicalSimEnabled_uiccSlotInfoIsNull_returnsFalse() {
+ mockTelephonyManager.stub { on { uiccSlotsInfo } doReturn arrayOf(null) }
+
+ val result = repository.anyRemovablePhysicalSimEnabled()
+
+ assertThat(result).isFalse()
+ }
+
+ private companion object {
+ fun createUiccSlotInfo(
+ isEuicc: Boolean,
+ isRemovable: Boolean,
+ logicalSlotIdx: Int,
+ isActive: Boolean
+ ) =
+ UiccSlotInfo(
+ isEuicc,
+ /* cardId = */ "123",
+ /* cardStateInfo = */ UiccSlotInfo.CARD_STATE_INFO_PRESENT,
+ /* isExtendedApduSupported = */ true,
+ isRemovable,
+ /* portList = */ listOf(
+ UiccPortInfo(/* iccId= */ "", /* portIndex= */ 0, logicalSlotIdx, isActive),
+ ),
+ )
+
+ fun createUiccSlotInfoForRemovableEsimMep(
+ logicalSlotIdx1: Int,
+ isActiveEsim1: Boolean,
+ logicalSlotIdx2: Int,
+ isActiveEsim2: Boolean,
+ ) =
+ UiccSlotInfo(
+ /* isEuicc = */ true,
+ /* cardId = */ "123",
+ /* cardStateInfo = */ UiccSlotInfo.CARD_STATE_INFO_PRESENT,
+ /* isExtendedApduSupported = */ true,
+ /* isRemovable = */ true,
+ /* portList = */ listOf(
+ UiccPortInfo(
+ /* iccId = */ "",
+ /* portIndex = */ 0,
+ /* logicalSlotIndex = */ logicalSlotIdx1,
+ /* isActive = */ isActiveEsim1),
+ UiccPortInfo(
+ /* iccId = */ "",
+ /* portIndex = */ 1,
+ /* logicalSlotIndex = */ logicalSlotIdx2,
+ /* isActive = */ isActiveEsim2),
+ ),
+ )
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt
new file mode 100644
index 0000000..3f72b2c
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.network.telephony.ims
+
+import android.content.Context
+import android.telephony.AccessNetworkConstants
+import android.telephony.ims.feature.MmTelFeature
+import android.telephony.ims.stub.ImsRegistrationImplBase
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class ImsFeatureRepositoryTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val mockProvisioningRepository = mock<ProvisioningRepository>()
+ private val mockImsMmTelRepository = mock<ImsMmTelRepository>()
+
+ @Test
+ fun isReadyFlow_notProvisioned_returnFalse() = runBlocking {
+ mockProvisioningRepository.stub {
+ onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn
+ flowOf(false)
+ }
+
+ val repository =
+ ImsFeatureRepository(
+ context = context,
+ subId = SUB_ID,
+ provisioningRepository = mockProvisioningRepository,
+ )
+
+ val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+ assertThat(isReady).isFalse()
+ }
+
+ @Test
+ fun isReadyFlow_notSupported_returnFalse() = runBlocking {
+ mockImsMmTelRepository.stub {
+ onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(false)
+ }
+
+ val repository =
+ ImsFeatureRepository(
+ context = context,
+ subId = SUB_ID,
+ imsMmTelRepository = mockImsMmTelRepository,
+ )
+
+ val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+ assertThat(isReady).isFalse()
+ }
+
+ @Test
+ fun isReadyFlow_provisionedAndSupported_returnFalse() = runBlocking {
+ mockProvisioningRepository.stub {
+ onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn flowOf(true)
+ }
+ mockImsMmTelRepository.stub {
+ onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(true)
+ }
+
+ val repository =
+ ImsFeatureRepository(
+ context = context,
+ subId = SUB_ID,
+ provisioningRepository = mockProvisioningRepository,
+ imsMmTelRepository = mockImsMmTelRepository,
+ )
+
+ val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+ assertThat(isReady).isTrue()
+ }
+
+ private companion object {
+ const val SUB_ID = 10
+ const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE
+ const val TECH = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+ const val TRANSPORT_TYPE = AccessNetworkConstants.TRANSPORT_TYPE_WLAN
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
index 0144f66..f0a23eb 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
@@ -55,7 +55,8 @@
on { getWiFiCallingMode(any()) } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN
}
- private val repository = WifiCallingRepository(context, SUB_ID, mockImsMmTelRepository)
+ private val repository =
+ WifiCallingRepository(context, SUB_ID, imsMmTelRepository = mockImsMmTelRepository)
@Test
fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() {
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt
index e5f8fb0..a952763 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt
@@ -22,9 +22,9 @@
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isEnabled
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
@@ -96,10 +96,9 @@
fun title_displayed() {
setContent()
- composeTestRule
- .onNodeWithText(context.getString(R.string.app_launch_supported_domain_urls_title))
- .assertIsDisplayed()
- .assertIsEnabled()
+ composeTestRule.waitUntilExists(
+ hasText(context.getString(R.string.app_launch_supported_domain_urls_title)) and
+ isEnabled())
}
@Test
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
index ad2ba55..1910153 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
@@ -19,23 +19,17 @@
import android.content.Context
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
-import android.view.KeyEvent.ACTION_DOWN
-import android.view.KeyEvent.KEYCODE_FORWARD_DEL
import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.NativeKeyEvent
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
-import androidx.compose.ui.test.performKeyPress
import androidx.compose.ui.test.performTextClearance
import androidx.compose.ui.test.performTextInput
import androidx.lifecycle.testing.TestLifecycleOwner
@@ -80,7 +74,6 @@
on { targetSubInfo }.doReturn(null)
on { availableSubInfoList }.doReturn(listOf())
on { activeSubInfoList }.doReturn(listOf())
- on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
index 1395ed4..fcd3e24 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
@@ -45,7 +45,6 @@
on { targetSubInfo }.doReturn(null)
on { availableSubInfoList }.doReturn(listOf())
on { activeSubInfoList }.doReturn(listOf())
- on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
index d9c762d..ef86ac5 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
@@ -46,7 +46,6 @@
on { targetSubInfo }.doReturn(null)
on { availableSubInfoList }.doReturn(listOf())
on { activeSubInfoList }.doReturn(listOf())
- on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
index 5b7778e..385bc42 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
@@ -69,7 +69,6 @@
on { targetSubInfo }.doReturn(null)
on { availableSubInfoList }.doReturn(listOf())
on { activeSubInfoList }.doReturn(listOf())
- on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingActivityTest.kt b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingActivityTest.kt
index 7410bb4..6fb4b84 100644
--- a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingActivityTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingActivityTest.kt
@@ -52,7 +52,7 @@
.setSpaPage(SpaSearchLandingSpaPage.newBuilder().setDestination(DESTINATION))
.build()
- SpaSearchLandingActivity.tryLaunch(context, key.toByteString().toStringUtf8())
+ SpaSearchLandingActivity.tryLaunch(context, key.encodeToString())
verify(context).startActivity(argThat { getStringExtra(KEY_DESTINATION) == DESTINATION })
}
@@ -70,7 +70,7 @@
BundleValue.newBuilder().setIntValue(ARGUMENT_VALUE).build()))
.build()
- SpaSearchLandingActivity.tryLaunch(context, key.toByteString().toStringUtf8())
+ SpaSearchLandingActivity.tryLaunch(context, key.encodeToString())
val intent = argumentCaptor<Intent> { verify(context).startActivity(capture()) }.firstValue
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
diff --git a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingKeyExtTest.kt b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingKeyExtTest.kt
new file mode 100644
index 0000000..f4a9ce6
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingKeyExtTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.search
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.spa.SpaSearchLanding.BundleValue
+import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
+import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SpaSearchLandingKeyExtTest {
+
+ @Test
+ fun encodeToString_thenDecode_shouldDecodeCorrectly() {
+ val encoded = KEY.encodeToString()
+
+ val decoded = decodeToSpaSearchLandingKey(encoded)
+
+ assertThat(decoded).isEqualTo(KEY)
+ }
+
+ @Test
+ fun decodeToSpaSearchLandingKey_badString_shouldReturnNull() {
+ val decoded = decodeToSpaSearchLandingKey("bad")
+
+ assertThat(decoded).isNull()
+ }
+
+ private companion object {
+ val KEY: SpaSearchLandingKey =
+ SpaSearchLandingKey.newBuilder()
+ .setFragment(
+ SpaSearchLandingFragment.newBuilder()
+ .setFragmentName("Destination")
+ .setPreferenceKey("preference_key")
+ .putArguments(
+ "argument_key", BundleValue.newBuilder().setIntValue(123).build()))
+ .build()
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchRepositoryTest.kt
index c38f22f..f97abe4 100644
--- a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchRepositoryTest.kt
@@ -23,7 +23,6 @@
import com.android.settings.spa.search.SpaSearchRepository.Companion.createSearchIndexableData
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.google.common.truth.Truth.assertThat
-import com.google.protobuf.ByteString
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -46,7 +45,7 @@
assertThat(searchIndexableData.targetClass).isEqualTo(pageProvider::class.java)
assertThat(dynamicRawDataToIndex).hasSize(1)
val rawData = dynamicRawDataToIndex[0]
- val key = SpaSearchLandingKey.parseFrom(ByteString.copyFromUtf8(rawData.key))
+ val key = decodeToSpaSearchLandingKey(rawData.key)
assertThat(key)
.isEqualTo(
SpaSearchLandingKey.newBuilder()
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 67a5957..691b611 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
@@ -16,7 +16,6 @@
package com.android.settings.fingerprint2.domain.interactor
-import android.content.Context
import android.content.Intent
import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.biometrics.SensorLocationInternal
@@ -30,23 +29,37 @@
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.CancellationSignal
import android.os.Handler
-import androidx.test.core.app.ApplicationProvider
import com.android.settings.biometrics.GatekeeperPasswordProvider
+import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintEnrollmentRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
-import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
-import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
-import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSettingsRepositoryImpl
+import com.android.settings.biometrics.fingerprint2.data.repository.UserRepo
+import com.android.settings.biometrics.fingerprint2.domain.interactor.AuthenticateInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.CanEnrollFingerprintsInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollFingerprintInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrolledFingerprintsInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.GenerateChallengeInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.RemoveFingerprintsInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.RenameFingerprintsInteractorImpl
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.CanEnrollFingerprintsInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrolledFingerprintsInteractor
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.GenerateChallengeInteractor
+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.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
+import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.password.ChooseLockSettingsHelper
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.launch
@@ -75,13 +88,28 @@
class FingerprintManagerInteractorTest {
@JvmField @Rule var rule = MockitoJUnit.rule()
- private lateinit var underTest: FingerprintManagerInteractor
- private var context: Context = ApplicationProvider.getApplicationContext()
+ private lateinit var enrolledFingerprintsInteractorUnderTest: EnrolledFingerprintsInteractor
+ private lateinit var generateChallengeInteractorUnderTest: GenerateChallengeInteractor
+ private lateinit var removeFingerprintsInteractorUnderTest: RemoveFingerprintInteractor
+ private lateinit var renameFingerprintsInteractorUnderTest: RenameFingerprintInteractor
+ private lateinit var authenticateInteractorImplUnderTest: AuthenticateInteractorImpl
+ private lateinit var canEnrollFingerprintsInteractorUnderTest: CanEnrollFingerprintsInteractor
+ private lateinit var enrollInteractorUnderTest: EnrollFingerprintInteractor
+
+ private val userId = 0
private var backgroundDispatcher = StandardTestDispatcher()
@Mock private lateinit var fingerprintManager: FingerprintManager
@Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider
private var testScope = TestScope(backgroundDispatcher)
+ private var backgroundScope = testScope.backgroundScope
+ private val flow: FingerprintFlow = Default
+ private val maxFingerprints = 5
+ private val currUser = MutableStateFlow(0)
+ private val userRepo =
+ object : UserRepo {
+ override val currentUser: Flow<Int> = currUser
+ }
@Before
fun setup() {
@@ -89,7 +117,7 @@
FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
- 5 /* maxEnrollmentsPerUser */,
+ maxFingerprints,
listOf<ComponentInfoInternal>(),
FingerprintSensorProperties.TYPE_POWER_BUTTON,
false /* halControlsIllumination */,
@@ -97,20 +125,37 @@
listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
)
.toFingerprintSensor()
+
val fingerprintSensorRepository =
object : FingerprintSensorRepository {
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensor)
+ override val hasSideFps: Flow<Boolean> = flowOf(false)
}
- underTest =
- FingerprintManagerInteractorImpl(
- context,
- backgroundDispatcher,
+ val settingsRepository = FingerprintSettingsRepositoryImpl(maxFingerprints)
+ val fingerprintEnrollmentRepository =
+ FingerprintEnrollmentRepositoryImpl(
fingerprintManager,
- fingerprintSensorRepository,
- gateKeeperPasswordProvider,
- FingerprintEnrollInteractorImpl(context, fingerprintManager, Default),
+ userRepo,
+ settingsRepository,
+ backgroundDispatcher,
+ backgroundScope,
)
+
+ enrolledFingerprintsInteractorUnderTest =
+ EnrolledFingerprintsInteractorImpl(fingerprintManager, userId)
+ generateChallengeInteractorUnderTest =
+ GenerateChallengeInteractorImpl(fingerprintManager, userId, gateKeeperPasswordProvider)
+ removeFingerprintsInteractorUnderTest =
+ RemoveFingerprintsInteractorImpl(fingerprintManager, userId)
+ renameFingerprintsInteractorUnderTest =
+ RenameFingerprintsInteractorImpl(fingerprintManager, userId, backgroundDispatcher)
+ authenticateInteractorImplUnderTest = AuthenticateInteractorImpl(fingerprintManager, userId)
+
+ canEnrollFingerprintsInteractorUnderTest =
+ CanEnrollFingerprintsInteractorImpl(fingerprintEnrollmentRepository)
+
+ enrollInteractorUnderTest = EnrollFingerprintInteractorImpl(userId, fingerprintManager, flow)
}
@Test
@@ -119,7 +164,8 @@
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
val emptyFingerprintList: List<Fingerprint> = emptyList()
- assertThat(underTest.enrolledFingerprints.last()).isEqualTo(emptyFingerprintList)
+ assertThat(enrolledFingerprintsInteractorUnderTest.enrolledFingerprints.last())
+ .isEqualTo(emptyFingerprintList)
}
@Test
@@ -129,7 +175,7 @@
val fingerprintList: List<Fingerprint> = listOf(expected)
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
- val list = underTest.enrolledFingerprints.last()
+ val list = enrolledFingerprintsInteractorUnderTest.enrolledFingerprints.last()
assertThat(list!!.size).isEqualTo(fingerprintList.size)
val actual = list[0]
assertThat(actual.name).isEqualTo(expected.name)
@@ -138,24 +184,51 @@
}
@Test
- fun testCanEnrollFingerprint() =
+ fun testCanEnrollFingerprintSucceeds() =
testScope.runTest {
- val fingerprintList1: List<Fingerprint> =
+ val fingerprintList: List<Fingerprint> =
listOf(
- Fingerprint("Finger 1,", 2, 3L),
- Fingerprint("Finger 2,", 3, 3L),
- Fingerprint("Finger 3,", 4, 3L),
+ Fingerprint("Finger 1", 2, 3L),
+ Fingerprint("Finger 2", 3, 3L),
+ Fingerprint("Finger 3", 4, 3L),
)
- val fingerprintList2: List<Fingerprint> =
- fingerprintList1.plus(
- listOf(Fingerprint("Finger 4,", 5, 3L), Fingerprint("Finger 5,", 6, 3L))
- )
- whenever(fingerprintManager.getEnrolledFingerprints(anyInt()))
- .thenReturn(fingerprintList1)
- .thenReturn(fingerprintList2)
+ whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
- assertThat(underTest.canEnrollFingerprints.last()).isTrue()
- assertThat(underTest.canEnrollFingerprints.last()).isFalse()
+ var result: Boolean? = null
+ val job =
+ testScope.launch {
+ canEnrollFingerprintsInteractorUnderTest.canEnrollFingerprints.collect { result = it }
+ }
+
+ runCurrent()
+ job.cancelAndJoin()
+
+ assertThat(result).isTrue()
+ }
+
+ @Test
+ fun testCanEnrollFingerprintFails() =
+ testScope.runTest {
+ val fingerprintList: List<Fingerprint> =
+ listOf(
+ Fingerprint("Finger 1", 2, 3L),
+ Fingerprint("Finger 2", 3, 3L),
+ Fingerprint("Finger 3", 4, 3L),
+ Fingerprint("Finger 4", 5, 3L),
+ Fingerprint("Finger 5", 6, 3L),
+ )
+ whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
+
+ var result: Boolean? = null
+ val job =
+ testScope.launch {
+ canEnrollFingerprintsInteractorUnderTest.canEnrollFingerprints.collect { result = it }
+ }
+
+ runCurrent()
+ job.cancelAndJoin()
+
+ assertThat(result).isFalse()
}
@Test
@@ -178,7 +251,8 @@
argumentCaptor()
var result: Pair<Long, ByteArray?>? = null
- val job = testScope.launch { result = underTest.generateChallenge(1L) }
+ val job =
+ testScope.launch { result = generateChallengeInteractorUnderTest.generateChallenge(1L) }
runCurrent()
verify(fingerprintManager).generateChallenge(anyInt(), capture(generateChallengeCallback))
@@ -201,7 +275,10 @@
var result: Boolean? = null
val job =
- testScope.launch { result = underTest.removeFingerprint(fingerprintViewModelToRemove) }
+ testScope.launch {
+ result =
+ removeFingerprintsInteractorUnderTest.removeFingerprint(fingerprintViewModelToRemove)
+ }
runCurrent()
verify(fingerprintManager)
@@ -224,7 +301,10 @@
var result: Boolean? = null
val job =
- testScope.launch { result = underTest.removeFingerprint(fingerprintViewModelToRemove) }
+ testScope.launch {
+ result =
+ removeFingerprintsInteractorUnderTest.removeFingerprint(fingerprintViewModelToRemove)
+ }
runCurrent()
verify(fingerprintManager)
@@ -246,7 +326,7 @@
testScope.runTest {
val fingerprintToRename = FingerprintData("Finger 2", 1, 2L)
- underTest.renameFingerprint(fingerprintToRename, "Woo")
+ renameFingerprintsInteractorUnderTest.renameFingerprint(fingerprintToRename, "Woo")
verify(fingerprintManager).rename(eq(fingerprintToRename.fingerId), anyInt(), safeEq("Woo"))
}
@@ -257,7 +337,7 @@
val fingerprint = Fingerprint("Woooo", 100, 101L)
var result: FingerprintAuthAttemptModel? = null
- val job = launch { result = underTest.authenticate() }
+ val job = launch { result = authenticateInteractorImplUnderTest.authenticate() }
val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> = argumentCaptor()
@@ -284,7 +364,7 @@
fun testAuth_lockout() =
testScope.runTest {
var result: FingerprintAuthAttemptModel? = null
- val job = launch { result = underTest.authenticate() }
+ val job = launch { result = authenticateInteractorImplUnderTest.authenticate() }
val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> = argumentCaptor()
@@ -314,7 +394,7 @@
val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null
val job = launch {
- underTest
+ enrollInteractorUnderTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it }
}
@@ -343,7 +423,7 @@
val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null
val job = launch {
- underTest
+ enrollInteractorUnderTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it }
}
@@ -372,7 +452,7 @@
val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null
val job = launch {
- underTest
+ enrollInteractorUnderTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it }
}
diff --git a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
index 9662c39..04cece8 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
@@ -99,9 +99,10 @@
backgroundViewModel.inForeground()
enrollViewModel =
FingerprintEnrollViewModel(
- fakeFingerprintManagerInteractor,
gatekeeperViewModel,
navigationViewModel,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
)
accessibilityInteractor =
object : AccessibilityInteractor {
diff --git a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/modules/enrolling/rfps/viewmodel/RFPSIconTouchViewModelTest.kt b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/modules/enrolling/rfps/viewmodel/RFPSIconTouchViewModelTest.kt
index 46e883a..53f4726 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/modules/enrolling/rfps/viewmodel/RFPSIconTouchViewModelTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/modules/enrolling/rfps/viewmodel/RFPSIconTouchViewModelTest.kt
@@ -49,8 +49,7 @@
fun setup() {
Dispatchers.setMain(backgroundDispatcher)
testScope = TestScope(backgroundDispatcher)
- rfpsIconTouchViewModel =
- RFPSIconTouchViewModel()
+ rfpsIconTouchViewModel = RFPSIconTouchViewModel()
}
@After
diff --git a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModelTest.kt b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModelTest.kt
index c475cc4..cf2deec 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModelTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModelTest.kt
@@ -88,9 +88,10 @@
backgroundViewModel.inForeground()
val fingerprintEnrollViewModel =
FingerprintEnrollViewModel(
- fakeFingerprintManagerInteractor,
gateKeeperViewModel,
navigationViewModel,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
)
enrollEnrollingViewModel =
FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel)
diff --git a/tests/unit/src/com/android/settings/fingerprint2/ui/settings/FingerprintSettingsNavigationViewModelTest.kt b/tests/unit/src/com/android/settings/fingerprint2/ui/settings/FingerprintSettingsNavigationViewModelTest.kt
index 201fffa..88f76dd 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/ui/settings/FingerprintSettingsNavigationViewModelTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/ui/settings/FingerprintSettingsNavigationViewModelTest.kt
@@ -67,10 +67,11 @@
underTest =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId,
- fakeFingerprintManagerInteractor,
backgroundDispatcher,
null,
null,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
)
.create(FingerprintSettingsNavigationViewModel::class.java)
}
@@ -272,10 +273,11 @@
underTest =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId,
- fakeFingerprintManagerInteractor,
backgroundDispatcher,
token,
challenge,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
)
.create(FingerprintSettingsNavigationViewModel::class.java)
@@ -299,10 +301,11 @@
underTest =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId,
- fakeFingerprintManagerInteractor,
backgroundDispatcher,
token,
challenge,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
)
.create(FingerprintSettingsNavigationViewModel::class.java)
@@ -331,10 +334,11 @@
underTest =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId,
- fakeFingerprintManagerInteractor,
backgroundDispatcher,
token,
challenge,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
)
.create(FingerprintSettingsNavigationViewModel::class.java)
diff --git a/tests/unit/src/com/android/settings/fingerprint2/ui/settings/FingerprintSettingsViewModelTest.kt b/tests/unit/src/com/android/settings/fingerprint2/ui/settings/FingerprintSettingsViewModelTest.kt
index 1618e16..79163d9 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/ui/settings/FingerprintSettingsViewModelTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/ui/settings/FingerprintSettingsViewModelTest.kt
@@ -73,19 +73,25 @@
navigationViewModel =
FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
defaultUserId,
- fakeFingerprintManagerInteractor,
backgroundDispatcher,
null,
null,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
)
.create(FingerprintSettingsNavigationViewModel::class.java)
underTest =
FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
defaultUserId,
- fakeFingerprintManagerInteractor,
backgroundDispatcher,
navigationViewModel,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
)
.create(FingerprintSettingsViewModel::class.java)
}
@@ -114,14 +120,7 @@
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(FingerprintData("a", 1, 3L))
- underTest =
- FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
- defaultUserId,
- fakeFingerprintManagerInteractor,
- backgroundDispatcher,
- navigationViewModel,
- )
- .create(FingerprintSettingsViewModel::class.java)
+ recreateSettingsViewModel()
var authAttempt: FingerprintAuthAttemptModel? = null
val job = launch { underTest.authFlow.take(5).collectLatest { authAttempt = it } }
@@ -156,14 +155,7 @@
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(FingerprintData("a", 1, 3L))
- underTest =
- FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
- defaultUserId,
- fakeFingerprintManagerInteractor,
- backgroundDispatcher,
- navigationViewModel,
- )
- .create(FingerprintSettingsViewModel::class.java)
+ recreateSettingsViewModel()
var authAttempt: FingerprintAuthAttemptModel? = null
val job = launch { underTest.authFlow.take(5).collectLatest { authAttempt = it } }
@@ -198,14 +190,7 @@
val success = FingerprintAuthAttemptModel.Success(1)
fakeFingerprintManagerInteractor.authenticateAttempt = success
- underTest =
- FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
- defaultUserId,
- fakeFingerprintManagerInteractor,
- backgroundDispatcher,
- navigationViewModel,
- )
- .create(FingerprintSettingsViewModel::class.java)
+ recreateSettingsViewModel()
var authAttempt: FingerprintAuthAttemptModel? = null
@@ -225,14 +210,7 @@
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(fingerprintToDelete)
- underTest =
- FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
- defaultUserId,
- fakeFingerprintManagerInteractor,
- backgroundDispatcher,
- navigationViewModel,
- )
- .create(FingerprintSettingsViewModel::class.java)
+ recreateSettingsViewModel()
var dialog: PreferenceViewModel? = null
val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } }
@@ -261,14 +239,7 @@
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(fingerprintToRename)
- underTest =
- FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
- defaultUserId,
- fakeFingerprintManagerInteractor,
- backgroundDispatcher,
- navigationViewModel,
- )
- .create(FingerprintSettingsViewModel::class.java)
+ recreateSettingsViewModel()
var dialog: PreferenceViewModel? = null
val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } }
@@ -299,14 +270,7 @@
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal =
mutableListOf(fingerprintToDelete)
- underTest =
- FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
- defaultUserId,
- fakeFingerprintManagerInteractor,
- backgroundDispatcher,
- navigationViewModel,
- )
- .create(FingerprintSettingsViewModel::class.java)
+ recreateSettingsViewModel()
var dialog: PreferenceViewModel? = null
val dialogJob = launch { underTest.isShowingDialog.collect { dialog = it } }
@@ -390,6 +354,22 @@
assertThat(authAttempt).isEqualTo(null)
}
+ private fun recreateSettingsViewModel() {
+ underTest =
+ FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
+ defaultUserId,
+ backgroundDispatcher,
+ navigationViewModel,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ fakeFingerprintManagerInteractor,
+ )
+ .create(FingerprintSettingsViewModel::class.java)
+ }
+
private fun setupAuth(): MutableList<FingerprintData> {
fakeFingerprintManagerInteractor.sensorProp =
FingerprintSensorPropertiesInternal(
@@ -409,14 +389,7 @@
val success = FingerprintAuthAttemptModel.Success(1)
fakeFingerprintManagerInteractor.authenticateAttempt = success
- underTest =
- FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
- defaultUserId,
- fakeFingerprintManagerInteractor,
- backgroundDispatcher,
- navigationViewModel,
- )
- .create(FingerprintSettingsViewModel::class.java)
+ recreateSettingsViewModel()
return fingerprints
}
diff --git a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
index 2aa1573..83a6fed 100644
--- a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
+++ b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
@@ -38,8 +38,6 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.google.common.collect.ImmutableList;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,7 +45,6 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -82,25 +79,6 @@
}
@Test
- public void getSlotInfos_oneSimSlotDevice_returnTheCorrectSlotInfoList() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(oneSimSlotDeviceActivePsim());
- ImmutableList<UiccSlotInfo> testUiccSlotInfos =
- UiccSlotUtil.getSlotInfos(mTelephonyManager);
-
- assertThat(testUiccSlotInfos.size()).isEqualTo(1);
- }
-
- @Test
- public void getSlotInfos_twoSimSlotsDevice_returnTheCorrectSlotInfoList() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- twoSimSlotsDeviceActivePsimActiveEsim());
- ImmutableList<UiccSlotInfo> testUiccSlotInfos =
- UiccSlotUtil.getSlotInfos(mTelephonyManager);
-
- assertThat(testUiccSlotInfos.size()).isEqualTo(2);
- }
-
- @Test
public void getEsimSlotId_twoSimSlotsDeviceAndEsimIsSlot0_returnTheCorrectEsimSlot() {
when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
twoSimSlotsDeviceActiveEsimActivePsim());
@@ -643,105 +621,7 @@
assertThat(testExcludedLogicalSlotIndex).isEqualTo(verifyExcludedLogicalSlotIndex);
}
- @Test
- public void isRemovableSimEnabled_noPsim_returnsFalse() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- oneSimSlotDeviceActiveEsim());
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isFalse();
- }
-
- @Test
- public void isRemovableSimEnabled_activeRemovableEsimAndInactivePsim_returnsFalse() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- twoSimSlotsDeviceActiveRemovableEsimInactivePsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isFalse();
- }
-
- @Test
- public void isRemovableSimEnabled_activeRemovableEsimAndActivePsim_returnsTrue() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- twoSimSlotsDeviceActivePsimActiveRemovableEsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isTrue();
- }
-
- @Test
- public void isRemovableSimEnabled_inactiveRemovableEsimAndActivePsim_returnsTrue() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- twoSimSlotsDeviceInactiveRemovableEsimActivePsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isTrue();
- }
-
- @Test
- public void isRemovableSimEnabled_twoActiveRemovableEsimsAndInactivePsim_returnsFalse() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- twoSimSlotsDeviceTwoActiveRemovableEsimsInactivePsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isFalse();
- }
-
- @Test
- public void isRemovableSimEnabled_oneActiveOneInactiveRemovableEsimActivePsim_returnsTrue() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- twoSimSlotsDeviceOneActiveOneInactiveRemovableEsimsActivePsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isTrue();
- }
-
- @Test
- public void isRemovableSimEnabled_activePsim_returnsTrue() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- oneSimSlotDeviceActivePsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isTrue();
- }
-
- @Test
- public void isRemovableSimEnabled_inactivePsim_returnsFalse() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- oneSimSlotDeviceinactivePsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isFalse();
- }
-
- @Test
- public void isRemovableSimEnabled_activeEsimAndActivePsim_returnsTrue() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- twoSimSlotsDeviceActivePsimActiveEsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isTrue();
- }
-
- @Test
- public void isRemovableSimEnabled_activeEsimAndInactivePsim_returnsFalse() {
- when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
- twoSimSlotsDeviceInactivePsimActiveEsim());
-
- boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
- assertThat(testSlot).isFalse();
- }
@Test
public void performSwitchToSlot_setSimSlotMapping() throws UiccSlotsException {
@@ -856,13 +736,6 @@
return slotMap;
}
- private List<UiccSlotMapping> createUiccSlotMappingSsModeEsimPort1Active() {
- List<UiccSlotMapping> slotMap = new ArrayList<>();
- slotMap.add(new UiccSlotMapping(1, ESIM_PHYSICAL_SLOT, 0));
-
- return slotMap;
- }
-
private List<UiccSlotMapping> createUiccSlotMappingPsimAndPort0() {
List<UiccSlotMapping> slotMap = new ArrayList<>();
slotMap.add(new UiccSlotMapping(0, PSIM_PHYSICAL_SLOT, 0));
@@ -915,14 +788,6 @@
return new UiccSlotInfo[]{createUiccSlotInfo(false, true, 0, true)};
}
- private UiccSlotInfo[] oneSimSlotDeviceActiveEsim() {
- return new UiccSlotInfo[]{createUiccSlotInfo(true, false, 1, true)};
- }
-
- private UiccSlotInfo[] oneSimSlotDeviceinactivePsim() {
- return new UiccSlotInfo[]{createUiccSlotInfo(false, true, -1, false)};
- }
-
private UiccSlotInfo[] twoSimSlotsDeviceActivePsimActiveEsim() {
return new UiccSlotInfo[]{
createUiccSlotInfo(false, true, 0, true),
@@ -941,61 +806,12 @@
createUiccSlotInfo(true, true, 1, true)};
}
- private UiccSlotInfo[] twoSimSlotsDeviceActiveRemovableEsimInactivePsim() {
- return new UiccSlotInfo[]{
- createUiccSlotInfo(true, true, 0, true),
- createUiccSlotInfo(false, true, -1, false)};
- }
-
- private UiccSlotInfo[] twoSimSlotsDeviceInactiveRemovableEsimActivePsim() {
- return new UiccSlotInfo[]{
- createUiccSlotInfo(true, true, -1, false),
- createUiccSlotInfo(false, true, 0, true)};
- }
-
- private UiccSlotInfo[] twoSimSlotsDeviceTwoActiveRemovableEsimsInactivePsim() {
- return new UiccSlotInfo[]{
- createUiccSlotInfoForRemovableEsimMep(0, true, 1, true),
- createUiccSlotInfo(false, true, -1, false)};
- }
-
- private UiccSlotInfo[] twoSimSlotsDeviceOneActiveOneInactiveRemovableEsimsActivePsim() {
- return new UiccSlotInfo[]{
- createUiccSlotInfoForRemovableEsimMep(1, true, -1, false),
- createUiccSlotInfo(false, true, 0, true)};
- }
-
private UiccSlotInfo[] twoSimSlotsDeviceActiveEsimActivePsim() {
return new UiccSlotInfo[]{
createUiccSlotInfo(true, false, 0, true),
createUiccSlotInfo(false, true, 1, true)};
}
- private UiccSlotInfo[] twoSimSlotsDeviceTwoActiveEsims() {
- // device supports MEP, so device can enable two esims.
- // If device has psim slot, the UiccSlotInfo of psim always be in UiccSlotInfo[].
- return new UiccSlotInfo[]{
- createUiccSlotInfo(false, true, -1, true),
- createUiccSlotInfoForEsimMep(0, true, 1, true)};
- }
-
- private UiccSlotInfo[] twoSimSlotsDeviceActivePsimInactiveEsim() {
- return new UiccSlotInfo[]{
- createUiccSlotInfo(false, true, 0, true),
- createUiccSlotInfo(true, false, -1, false)};
- }
-
- private UiccSlotInfo[] twoSimSlotsDeviceInactivePsimActiveEsim() {
- return new UiccSlotInfo[]{
- createUiccSlotInfo(false, true, 0, false),
- createUiccSlotInfo(true, false, 1, true)};
- }
-
- private UiccSlotInfo[] twoSimSlotsDeviceNoInsertPsimActiveEsim() {
- return new UiccSlotInfo[]{
- createUiccSlotInfo(false, true, -1, false),
- createUiccSlotInfo(true, false, 1, true)};
- }
//ToDo: add more cases.
private UiccSlotInfo createUiccSlotInfo(boolean isEuicc, boolean isRemovable,
@@ -1011,36 +827,4 @@
logicalSlotIdx /* logicalSlotIdx */, isActive /* isActive */))
);
}
-
- private UiccSlotInfo createUiccSlotInfoForEsimMep(int logicalSlotIdx1, boolean isActiveEsim1,
- int logicalSlotIdx2, boolean isActiveEsim2) {
- return new UiccSlotInfo(
- true, /* isEuicc */
- "123", /* cardId */
- CARD_STATE_INFO_PRESENT, /* cardStateInfo */
- true, /* isExtendApduSupported */
- false, /* isRemovable */
- Arrays.asList(
- new UiccPortInfo("" /* iccId */, 0 /* portIdx */,
- logicalSlotIdx1 /* logicalSlotIdx */, isActiveEsim1 /* isActive */),
- new UiccPortInfo("" /* iccId */, 1 /* portIdx */,
- logicalSlotIdx2 /* logicalSlotIdx */,
- isActiveEsim2 /* isActive */)));
- }
-
- private UiccSlotInfo createUiccSlotInfoForRemovableEsimMep(int logicalSlotIdx1,
- boolean isActiveEsim1, int logicalSlotIdx2, boolean isActiveEsim2) {
- return new UiccSlotInfo(
- true, /* isEuicc */
- "123", /* cardId */
- CARD_STATE_INFO_PRESENT, /* cardStateInfo */
- true, /* isExtendApduSupported */
- true, /* isRemovable */
- Arrays.asList(
- new UiccPortInfo("" /* iccId */, 0 /* portIdx */,
- logicalSlotIdx1 /* logicalSlotIdx */, isActiveEsim1 /* isActive */),
- new UiccPortInfo("" /* iccId */, 1 /* portIdx */,
- logicalSlotIdx2 /* logicalSlotIdx */,
- isActiveEsim2 /* isActive */)));
- }
}
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 3571a36..4765d18 100644
--- a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java
+++ b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java
@@ -118,12 +118,12 @@
mRepository.mSecurityType = mSecurityType;
mRepository.mSpeedType = mSpeedType;
mRepository.mIsDualBand = true;
- mRepository.mBand5g.isUsableChannelsReady = true;
- mRepository.mBand5g.isUsableChannelsUnsupported = false;
- mRepository.mBand5g.hasUsableChannels = true;
- mRepository.mBand6g.isUsableChannelsReady = true;
- mRepository.mBand6g.isUsableChannelsUnsupported = false;
- mRepository.mBand6g.hasUsableChannels = true;
+ mRepository.mBand5g.isChannelsReady = true;
+ mRepository.mBand5g.isChannelsUnsupported = false;
+ mRepository.mBand5g.hasChannels = true;
+ mRepository.mBand6g.isChannelsReady = true;
+ mRepository.mBand6g.isChannelsUnsupported = false;
+ mRepository.mBand6g.hasChannels = true;
}
@Test
@@ -382,7 +382,7 @@
@Test
public void updateSpeedType_singleBand5gPreferredBut5gUnavailable_get2gSpeedType() {
mRepository.mIsDualBand = false;
- mRepository.mBand5g.hasUsableChannels = false;
+ mRepository.mBand5g.hasChannels = false;
SoftApConfiguration config = new SoftApConfiguration.Builder()
.setBand(WIFI_5GHZ_BAND_PREFERRED).build();
when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
@@ -407,7 +407,7 @@
@Test
public void updateSpeedType_singleBand6gPreferredBut6gUnavailable_get5gSpeedType() {
mRepository.mIsDualBand = false;
- mRepository.mBand6g.hasUsableChannels = false;
+ mRepository.mBand6g.hasChannels = false;
SoftApConfiguration config = new SoftApConfiguration.Builder()
.setBand(WIFI_6GHZ_BAND_PREFERRED).build();
when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
@@ -420,8 +420,8 @@
@Test
public void updateSpeedType_singleBand6gPreferredBut5gAnd6gUnavailable_get2gSpeedType() {
mRepository.mIsDualBand = false;
- mRepository.mBand5g.hasUsableChannels = false;
- mRepository.mBand6g.hasUsableChannels = false;
+ mRepository.mBand5g.hasChannels = false;
+ mRepository.mBand6g.hasChannels = false;
SoftApConfiguration config = new SoftApConfiguration.Builder()
.setBand(WIFI_6GHZ_BAND_PREFERRED).build();
when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
@@ -446,7 +446,7 @@
@Test
public void updateSpeedType_dualBand2gAnd5gBut5gUnavailable_get2gSpeedType() {
mRepository.mIsDualBand = true;
- mRepository.mBand5g.hasUsableChannels = false;
+ mRepository.mBand5g.hasChannels = false;
SoftApConfiguration config = new SoftApConfiguration.Builder()
.setBand(WIFI_5GHZ_BAND_PREFERRED).build();
when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
@@ -562,19 +562,19 @@
}
@Test
- public void is5gAvailable_hasUsableChannels_returnTrue() {
+ public void is5gAvailable_hasChannels_returnTrue() {
mRepository.mIs5gBandSupported = true;
// Reset m5gBand to trigger an update
- mRepository.mBand5g.isUsableChannelsReady = false;
+ mRepository.mBand5g.isChannelsReady = false;
assertThat(mRepository.is5gAvailable()).isTrue();
}
@Test
- public void is5gAvailable_noUsableChannels_returnFalse() {
+ public void is5gAvailable_noChannels_returnFalse() {
mRepository.mIs5gBandSupported = true;
// Reset m5gBand to trigger an update
- mRepository.mBand5g.isUsableChannelsReady = false;
+ mRepository.mBand5g.isChannelsReady = false;
when(mWifiManager.getAllowedChannels(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, OP_MODE_SAP))
.thenReturn(null);
@@ -585,7 +585,7 @@
@UiThreadTest
public void get5gAvailable_shouldNotReturnNull() {
// Reset m5gBand to trigger an update
- mRepository.mBand5g.isUsableChannelsReady = false;
+ mRepository.mBand5g.isChannelsReady = false;
assertThat(mRepository.get5gAvailable()).isNotNull();
}
@@ -606,19 +606,19 @@
}
@Test
- public void is6gAvailable_hasUsableChannels_returnTrue() {
+ public void is6gAvailable_hasChannels_returnTrue() {
mRepository.mIs6gBandSupported = true;
// Reset m6gBand to trigger an update
- mRepository.mBand6g.isUsableChannelsReady = false;
+ mRepository.mBand6g.isChannelsReady = false;
assertThat(mRepository.is6gAvailable()).isTrue();
}
@Test
- public void is6gAvailable_noUsableChannels_returnFalse() {
+ public void is6gAvailable_noChannels_returnFalse() {
mRepository.mIs6gBandSupported = true;
// Reset m6gBand to trigger an update
- mRepository.mBand6g.isUsableChannelsReady = false;
+ mRepository.mBand6g.isChannelsReady = false;
when(mWifiManager.getAllowedChannels(WifiScanner.WIFI_BAND_6_GHZ, OP_MODE_SAP))
.thenReturn(null);
@@ -658,33 +658,33 @@
}
@Test
- public void isChannelAvailable_throwIllegalArgumentException_hasUsableChannelsFalse() {
+ public void isChannelAvailable_throwIllegalArgumentException_hasChannelsFalse() {
doThrow(IllegalArgumentException.class).when(mWifiManager)
.getAllowedChannels(WifiScanner.WIFI_BAND_6_GHZ, OP_MODE_SAP);
mRepository.isChannelAvailable(mRepository.mBand6g);
- assertThat(mRepository.mBand6g.hasUsableChannels).isFalse();
- assertThat(mRepository.mBand6g.isUsableChannelsUnsupported).isTrue();
+ assertThat(mRepository.mBand6g.hasChannels).isFalse();
+ assertThat(mRepository.mBand6g.isChannelsUnsupported).isTrue();
}
@Test
- public void isChannelAvailable_throwUnsupportedOperationException_hasUsableChannelsFalse() {
+ public void isChannelAvailable_throwUnsupportedOperationException_hasChannelsFalse() {
doThrow(UnsupportedOperationException.class).when(mWifiManager)
.getAllowedChannels(WifiScanner.WIFI_BAND_6_GHZ, OP_MODE_SAP);
mRepository.isChannelAvailable(mRepository.mBand6g);
- assertThat(mRepository.mBand6g.hasUsableChannels).isFalse();
- assertThat(mRepository.mBand6g.isUsableChannelsUnsupported).isTrue();
+ assertThat(mRepository.mBand6g.hasChannels).isFalse();
+ assertThat(mRepository.mBand6g.isChannelsUnsupported).isTrue();
}
@Test
- public void isChannelAvailable_noExceptionAndHasUsableChannels_hasUsableChannelsTrue() {
+ public void isChannelAvailable_noExceptionAndHasChannels_hasChannelsTrue() {
mRepository.isChannelAvailable(mRepository.mBand6g);
- assertThat(mRepository.mBand6g.hasUsableChannels).isTrue();
- assertThat(mRepository.mBand6g.isUsableChannelsUnsupported).isFalse();
+ assertThat(mRepository.mBand6g.hasChannels).isTrue();
+ assertThat(mRepository.mBand6g.isChannelsUnsupported).isFalse();
}
@Test
@@ -744,9 +744,9 @@
}
@Test
- public void updateCapabilityChanged_band5gUsableChannelsUnsupported_update5gAvailable() {
+ public void updateCapabilityChanged_band5gChannelsUnsupported_update5gAvailable() {
mRepository = spy(new WifiHotspotRepository(mContext, mWifiManager, mTetheringManager));
- mRepository.mBand5g.isUsableChannelsUnsupported = true;
+ mRepository.mBand5g.isChannelsUnsupported = true;
mRepository.updateCapabilityChanged();
@@ -755,9 +755,9 @@
}
@Test
- public void updateCapabilityChanged_band6gUsableChannelsUnsupported_update5gAvailable() {
+ public void updateCapabilityChanged_band6gChannelsUnsupported_update5gAvailable() {
mRepository = spy(new WifiHotspotRepository(mContext, mWifiManager, mTetheringManager));
- mRepository.mBand6g.isUsableChannelsUnsupported = true;
+ mRepository.mBand6g.isChannelsUnsupported = true;
mRepository.updateCapabilityChanged();
@@ -766,18 +766,18 @@
}
@Test
- public void isAvailable_isUsableChannelsUnsupportedFalse_returnHasUsableChannels() {
- mRepository.mBand6g.isUsableChannelsUnsupported = false;
- mRepository.mBand6g.hasUsableChannels = false;
+ public void isAvailable_isChannelsUnsupportedFalse_returnHasChannels() {
+ mRepository.mBand6g.isChannelsUnsupported = false;
+ mRepository.mBand6g.hasChannels = false;
mRepository.mBand6g.hasCapability = true;
assertThat(mRepository.mBand6g.isAvailable()).isFalse();
}
@Test
- public void isAvailable_isUsableChannelsUnsupportedTrue_returnHasCapability() {
- mRepository.mBand6g.isUsableChannelsUnsupported = true;
- mRepository.mBand6g.hasUsableChannels = false;
+ public void isAvailable_isChannelsUnsupportedTrue_returnHasCapability() {
+ mRepository.mBand6g.isChannelsUnsupported = true;
+ mRepository.mBand6g.hasChannels = false;
mRepository.mBand6g.hasCapability = true;
assertThat(mRepository.mBand6g.isAvailable()).isTrue();