Merge "Revert "Stop running face detection on bouncer if both face and fp are enrolled."" into tm-qpr-dev
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index d755d38..eac3bee 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -87,6 +87,14 @@
public static final int MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR = 2;
/**
+ * Feature flag for Attention Service.
+ *
+ * TODO(b/247920386): Add TestApi annotation
+ * @hide
+ */
+ public static final boolean ENABLE_PROXIMITY_RESULT = false;
+
+ /**
* Indicates that the updated status is successful.
*/
public static final int INITIALIZATION_STATUS_SUCCESS = 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index ff4b2ed..f879994 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -24,6 +24,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.TaskInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
import android.util.Slog;
@@ -327,6 +328,28 @@
return recentTasks;
}
+ /**
+ * Find the background task that match the given component.
+ */
+ @Nullable
+ public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName) {
+ if (componentName == null) {
+ return null;
+ }
+ List<ActivityManager.RecentTaskInfo> tasks = getRawRecentTasks(Integer.MAX_VALUE,
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser());
+ for (int i = 0; i < tasks.size(); i++) {
+ final ActivityManager.RecentTaskInfo task = tasks.get(i);
+ if (task.isVisible) {
+ continue;
+ }
+ if (componentName.equals(task.baseIntent.getComponent())) {
+ return task;
+ }
+ }
+ return null;
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 991f136..07a6895 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -385,6 +385,9 @@
}
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ mStageCoordinator.prepareEvictInvisibleChildTasks(evictWct);
+ mSyncQueue.queue(evictWct);
}
};
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
@@ -472,8 +475,16 @@
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
- // split.
+ // split and there is no reusable background task.
if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
+ final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional.isPresent()
+ ? mRecentTasksOptional.get().findTaskInBackground(
+ intent.getIntent().getComponent())
+ : null;
+ if (taskInfo != null) {
+ startTask(taskInfo.taskId, position, options);
+ return;
+ }
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
}
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index f448fbf..9cdd103 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Трансліруйце змесціва праграм з вашага тэлефона"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> мець доступ да гэтай інфармацыі з вашага тэлефона"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Сэрвісы для некалькіх прылад"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на перадачу праграм плынню паміж вашымі прыладамі"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на трансляцыю праграм паміж вашымі прыладамі"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> мець доступ да гэтай інфармацыі з вашага тэлефона"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index c781ea1..6d9ad9d 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -23,12 +23,12 @@
<string name="summary_watch" msgid="3002344206574997652">"Se necesita esta aplicación para gestionar tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> podrá interactuar con tus notificaciones y acceder a tus permisos de teléfono, SMS, contactos, calendario, registros de llamadas y dispositivos cercanos."</string>
<string name="permission_apps" msgid="6142133265286656158">"Aplicaciones"</string>
<string name="permission_apps_summary" msgid="798718816711515431">"Proyecta aplicaciones de tu teléfono"</string>
- <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde tu teléfono"</string>
+ <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu teléfono"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string>
<string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para emitir aplicaciones en otros dispositivos tuyos"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
- <string name="title_computer" msgid="4693714143506569253">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde tu teléfono"</string>
+ <string name="title_computer" msgid="4693714143506569253">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu teléfono"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"Notificaciones"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"Puede leer todas las notificaciones, incluida información como contactos, mensajes y fotos"</string>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index 3997deb..6f28275 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -25,13 +25,13 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streymdu forritum símans"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að þessum upplýsingum úr símanum þínum"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Þjónustur á milli tækja"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um heimild fyrir straumspilun forrita á milli tækjanna þinna fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um heimild til straumspilunar forrita á milli tækjanna þinna fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að þessum upplýsingum úr símanum þínum"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"Tilkynningar"</string>
- <string name="permission_notification_summary" msgid="884075314530071011">"Getur lesið allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði skilaboð og myndir"</string>
+ <string name="permission_notification_summary" msgid="884075314530071011">"Getur lesið allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði, skilaboð og myndir"</string>
<string name="permission_storage" msgid="6831099350839392343">"Myndir og efni"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Þjónusta Google Play"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index 24a3094..3e7b023 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -25,14 +25,14 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Телефондогу колдонмолорду алып ойнотуу"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Түзмөктөр аралык кызматтар"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду тышкы экранга чыгарууга уруксат сурап жатат"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду өткөрүүгө уруксат сурап жатат"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"Билдирмелер"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"Бардык билдирмелерди, анын ичинде байланыштар, билдирүүлөр жана сүрөттөр сыяктуу маалыматты окуй алат"</string>
- <string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиа"</string>
+ <string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиафайлдар"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play кызматтары"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан телефондогу сүрөттөрдү, медиа файлдарды жана билдирмелерди колдонууга уруксат сурап жатат"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index fa7155e..a7d8c5e 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streamovať aplikácie telefónu"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pre viacero zariadení"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 24465fc..966ff55 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Tiririsha programu za simu yako"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie maelezo haya kutoka kwenye simu yako"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Huduma za kifaa kilichounganishwa kwingine"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili itiririshe programu kati ya vifaa vyako"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili itiririshe programu kati ya vifaa vyako"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie maelezo haya kutoka kwenye simu yako"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Picha na maudhui"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Huduma za Google Play"</string>
- <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za simu yako"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za simu yako"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index 7440079..cd96095 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -31,7 +31,7 @@
<string name="title_computer" msgid="4693714143506569253">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> యాప్ను అనుమతించండి"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"నోటిఫికేషన్లు"</string>
- <string name="permission_notification_summary" msgid="884075314530071011">"కాంటాక్ట్లు, మెసేజ్లు, ఫోటోల వంటి సమాచారంతో సహా అన్ని నోటిఫికేషన్లను చదవగలరు"</string>
+ <string name="permission_notification_summary" msgid="884075314530071011">"కాంటాక్ట్లు, మెసేజ్లు, ఫోటోల వంటి సమాచారంతో సహా అన్ని నోటిఫికేషన్లను చదవగలదు"</string>
<string name="permission_storage" msgid="6831099350839392343">"ఫోటోలు, మీడియా"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play సర్వీసులు"</string>
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 8182484..9f275af 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -813,7 +813,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
@@ -828,7 +828,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 0c57b934..8388b67 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -103,6 +103,7 @@
android:layout_width="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
+ android:visibility="invisible"
android:clipChildren="false"
android:clipToPadding="false" />
</LinearLayout>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index f73c98e..2bdb1b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -22,8 +22,6 @@
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
import static java.lang.Integer.max;
@@ -87,8 +85,8 @@
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter;
import com.android.systemui.user.data.source.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
@@ -1098,6 +1096,7 @@
return;
}
+ mView.setAlpha(1f);
mUserSwitcherViewGroup.setAlpha(0f);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
1f);
@@ -1137,7 +1136,7 @@
KeyguardUserSwitcherAnchor anchor = mView.findViewById(R.id.user_switcher_anchor);
- BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) {
+ BaseUserSwitcherAdapter adapter = new BaseUserSwitcherAdapter(mUserSwitcherController) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
UserRecord item = getItem(position);
@@ -1172,8 +1171,7 @@
}
textView.setSelected(item == currentUser);
view.setEnabled(item.isSwitchToEnabled);
- view.setAlpha(view.isEnabled() ? USER_SWITCH_ENABLED_ALPHA :
- USER_SWITCH_DISABLED_ALPHA);
+ UserSwitcherController.setSelectableAlpha(view);
return view;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index d8cffd7..5995e85 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -107,6 +107,14 @@
}
@Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+ }
+
+ @Override
void resetState() {
super.resetState();
mStateMachine.reset();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 0469152..443d277 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -41,6 +41,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyguard.data.BouncerViewModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.media.dagger.MediaProjectionModule;
import com.android.systemui.model.SysUiState;
@@ -116,6 +117,7 @@
AppOpsModule.class,
AssistModule.class,
BiometricsModule.class,
+ BouncerViewModule.class,
ClockModule.class,
CoroutinesModule.class,
DreamModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index d7b7777..733a80d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -35,6 +35,7 @@
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -73,6 +74,7 @@
// Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
private final Handler mHandler;
private final int mDreamOverlayMaxTranslationY;
+ private final BouncerCallbackInteractor mBouncerCallbackInteractor;
private long mJitterStartTimeMillis;
@@ -131,7 +133,8 @@
@Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
@Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
burnInProtectionUpdateInterval,
- @Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter) {
+ @Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter,
+ BouncerCallbackInteractor bouncerCallbackInteractor) {
super(containerView);
mDreamOverlayContentView = contentView;
mStatusBarViewController = statusBarViewController;
@@ -151,6 +154,7 @@
mMaxBurnInOffset = maxBurnInOffset;
mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
mMillisUntilFullJitter = millisUntilFullJitter;
+ mBouncerCallbackInteractor = bouncerCallbackInteractor;
}
@Override
@@ -167,6 +171,7 @@
if (bouncer != null) {
bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
}
+ mBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
}
@Override
@@ -176,6 +181,7 @@
if (bouncer != null) {
bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
}
+ mBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
}
View getContainerView() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java
index 21a51d1..c07d402 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java
@@ -18,13 +18,21 @@
import static com.android.systemui.dreams.complication.dagger.DreamMediaEntryComplicationComponent.DreamMediaEntryModule.DREAM_MEDIA_ENTRY_VIEW;
import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_MEDIA_ENTRY_LAYOUT_PARAMS;
+import static com.android.systemui.flags.Flags.DREAM_MEDIA_TAP_TO_OPEN;
+import android.app.PendingIntent;
import android.util.Log;
import android.view.View;
+import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.dagger.DreamMediaEntryComplicationComponent;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -87,6 +95,15 @@
private final DreamOverlayStateController mDreamOverlayStateController;
private final MediaDreamComplication mMediaComplication;
+ private final MediaCarouselController mMediaCarouselController;
+
+ private final ActivityStarter mActivityStarter;
+ private final ActivityIntentHelper mActivityIntentHelper;
+ private final KeyguardStateController mKeyguardStateController;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+
+ private final FeatureFlags mFeatureFlags;
+ private boolean mIsTapToOpenEnabled;
private boolean mMediaComplicationAdded;
@@ -94,15 +111,28 @@
DreamMediaEntryViewController(
@Named(DREAM_MEDIA_ENTRY_VIEW) View view,
DreamOverlayStateController dreamOverlayStateController,
- MediaDreamComplication mediaComplication) {
+ MediaDreamComplication mediaComplication,
+ MediaCarouselController mediaCarouselController,
+ ActivityStarter activityStarter,
+ ActivityIntentHelper activityIntentHelper,
+ KeyguardStateController keyguardStateController,
+ NotificationLockscreenUserManager lockscreenUserManager,
+ FeatureFlags featureFlags) {
super(view);
mDreamOverlayStateController = dreamOverlayStateController;
mMediaComplication = mediaComplication;
+ mMediaCarouselController = mediaCarouselController;
+ mActivityStarter = activityStarter;
+ mActivityIntentHelper = activityIntentHelper;
+ mKeyguardStateController = keyguardStateController;
+ mLockscreenUserManager = lockscreenUserManager;
+ mFeatureFlags = featureFlags;
mView.setOnClickListener(this::onClickMediaEntry);
}
@Override
protected void onViewAttached() {
+ mIsTapToOpenEnabled = mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN);
}
@Override
@@ -113,6 +143,31 @@
private void onClickMediaEntry(View v) {
if (DEBUG) Log.d(TAG, "media entry complication tapped");
+ if (mIsTapToOpenEnabled) {
+ final PendingIntent clickIntent =
+ mMediaCarouselController.getCurrentVisibleMediaContentIntent();
+
+ if (clickIntent == null) {
+ return;
+ }
+
+ // See StatusBarNotificationActivityStarter#onNotificationClicked
+ final boolean showOverLockscreen = mKeyguardStateController.isShowing()
+ && mActivityIntentHelper.wouldShowOverLockscreen(clickIntent.getIntent(),
+ mLockscreenUserManager.getCurrentUserId());
+
+ if (showOverLockscreen) {
+ mActivityStarter.startActivity(clickIntent.getIntent(),
+ /* dismissShade */ true,
+ /* animationController */ null,
+ /* showOverLockscreenWhenLocked */ true);
+ } else {
+ mActivityStarter.postStartActivityDismissingKeyguard(clickIntent, null);
+ }
+
+ return;
+ }
+
if (!mMediaComplicationAdded) {
addMediaComplication();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 44feac1..48f5f9e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -104,6 +104,10 @@
public static final UnreleasedFlag MODERN_USER_SWITCHER_ACTIVITY =
new UnreleasedFlag(209, true);
+ /** Whether the new implementation of UserSwitcherController should be used. */
+ public static final UnreleasedFlag REFACTORED_USER_SWITCHER_CONTROLLER =
+ new UnreleasedFlag(210, false);
+
/***************************************/
// 300 - power menu
public static final ReleasedFlag POWER_MENU_LITE =
@@ -196,7 +200,8 @@
public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901);
public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903);
public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904);
- public static final UnreleasedFlag MEDIA_DREAM_COMPLICATION = new UnreleasedFlag(905);
+ public static final UnreleasedFlag DREAM_MEDIA_COMPLICATION = new UnreleasedFlag(905);
+ public static final UnreleasedFlag DREAM_MEDIA_TAP_TO_OPEN = new UnreleasedFlag(906);
// 1000 - dock
public static final ReleasedFlag SIMULATE_DOCK_THROUGH_CHARGING =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
new file mode 100644
index 0000000..99ae85d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.data
+
+import android.view.KeyEvent
+import com.android.systemui.dagger.SysUISingleton
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+
+/** An abstraction to interface with the ui layer, without changing state. */
+interface BouncerView {
+ var delegate: BouncerViewDelegate?
+}
+
+/** A lightweight class to hold reference to the ui delegate. */
+@SysUISingleton
+class BouncerViewImpl @Inject constructor() : BouncerView {
+ private var _delegate: WeakReference<BouncerViewDelegate?> = WeakReference(null)
+ override var delegate: BouncerViewDelegate?
+ get() = _delegate.get()
+ set(value) {
+ _delegate = WeakReference(value)
+ }
+}
+
+/** An abstraction that implements view logic. */
+interface BouncerViewDelegate {
+ fun isFullScreenBouncer(): Boolean
+ fun shouldDismissOnMenuPressed(): Boolean
+ fun interceptMediaKey(event: KeyEvent?): Boolean
+ fun dispatchBackKeyEventPreIme(): Boolean
+ fun showNextSecurityScreenOrFinish(): Boolean
+ fun resume()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
new file mode 100644
index 0000000..390c54e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface BouncerViewModule {
+ /** Binds BouncerView to BouncerViewImpl and makes it injectable. */
+ @Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
new file mode 100644
index 0000000..543389e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.hardware.biometrics.BiometricSourceType
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.keyguard.ViewMediatorCallback
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Encapsulates app state for the lock screen bouncer. */
+@SysUISingleton
+class KeyguardBouncerRepository
+@Inject
+constructor(
+ private val viewMediatorCallback: ViewMediatorCallback,
+ keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+ var bouncerPromptReason: Int? = null
+ /** Determines if we want to instantaneously show the bouncer instead of translating. */
+ private val _isScrimmed = MutableStateFlow(false)
+ val isScrimmed = _isScrimmed.asStateFlow()
+ /** Set amount of how much of the bouncer is showing on the screen */
+ private val _expansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
+ val expansionAmount = _expansionAmount.asStateFlow()
+ private val _isVisible = MutableStateFlow(false)
+ val isVisible = _isVisible.asStateFlow()
+ private val _show = MutableStateFlow<KeyguardBouncerModel?>(null)
+ val show = _show.asStateFlow()
+ private val _showingSoon = MutableStateFlow(false)
+ val showingSoon = _showingSoon.asStateFlow()
+ private val _hide = MutableStateFlow(false)
+ val hide = _hide.asStateFlow()
+ private val _startingToHide = MutableStateFlow(false)
+ val startingToHide = _startingToHide.asStateFlow()
+ private val _onDismissAction = MutableStateFlow<BouncerCallbackActionsModel?>(null)
+ val onDismissAction = _onDismissAction.asStateFlow()
+ private val _disappearAnimation = MutableStateFlow<Runnable?>(null)
+ val startingDisappearAnimation = _disappearAnimation.asStateFlow()
+ private val _keyguardPosition = MutableStateFlow(0f)
+ val keyguardPosition = _keyguardPosition.asStateFlow()
+ private val _resourceUpdateRequests = MutableStateFlow(false)
+ val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
+ private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
+ val showMessage = _showMessage.asStateFlow()
+ private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
+ /** Determines if user is already unlocked */
+ val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
+ private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
+ val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
+ private val _onScreenTurnedOff = MutableStateFlow(false)
+ val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
+
+ val bouncerErrorMessage: CharSequence?
+ get() = viewMediatorCallback.consumeCustomMessage()
+
+ init {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onStrongAuthStateChanged(userId: Int) {
+ bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
+ }
+
+ override fun onLockedOutStateChanged(type: BiometricSourceType) {
+ if (type == BiometricSourceType.FINGERPRINT) {
+ bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
+ }
+ }
+ }
+
+ keyguardUpdateMonitor.registerCallback(callback)
+ }
+
+ fun setScrimmed(isScrimmed: Boolean) {
+ _isScrimmed.value = isScrimmed
+ }
+
+ fun setExpansion(expansion: Float) {
+ _expansionAmount.value = expansion
+ }
+
+ fun setVisible(isVisible: Boolean) {
+ _isVisible.value = isVisible
+ }
+
+ fun setShow(keyguardBouncerModel: KeyguardBouncerModel?) {
+ _show.value = keyguardBouncerModel
+ }
+
+ fun setShowingSoon(showingSoon: Boolean) {
+ _showingSoon.value = showingSoon
+ }
+
+ fun setHide(hide: Boolean) {
+ _hide.value = hide
+ }
+
+ fun setStartingToHide(startingToHide: Boolean) {
+ _startingToHide.value = startingToHide
+ }
+
+ fun setOnDismissAction(bouncerCallbackActionsModel: BouncerCallbackActionsModel?) {
+ _onDismissAction.value = bouncerCallbackActionsModel
+ }
+
+ fun setStartDisappearAnimation(runnable: Runnable?) {
+ _disappearAnimation.value = runnable
+ }
+
+ fun setKeyguardPosition(keyguardPosition: Float) {
+ _keyguardPosition.value = keyguardPosition
+ }
+
+ fun setResourceUpdateRequests(willUpdateResources: Boolean) {
+ _resourceUpdateRequests.value = willUpdateResources
+ }
+
+ fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
+ _showMessage.value = bouncerShowMessageModel
+ }
+
+ fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
+ _keyguardAuthenticated.value = keyguardAuthenticated
+ }
+
+ fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
+ _isBackButtonEnabled.value = isBackButtonEnabled
+ }
+
+ fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
+ _onScreenTurnedOff.value = onScreenTurnedOff
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt
new file mode 100644
index 0000000..10c7a37
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+import com.android.systemui.util.ListenerSet
+import javax.inject.Inject
+
+/** Interactor to add and remove callbacks for the bouncer. */
+@SysUISingleton
+class BouncerCallbackInteractor @Inject constructor() {
+ private var resetCallbacks = ListenerSet<KeyguardBouncer.KeyguardResetCallback>()
+ private var expansionCallbacks = ArrayList<KeyguardBouncer.BouncerExpansionCallback>()
+ /** Add a KeyguardResetCallback. */
+ fun addKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
+ resetCallbacks.addIfAbsent(callback)
+ }
+
+ /** Remove a KeyguardResetCallback. */
+ fun removeKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
+ resetCallbacks.remove(callback)
+ }
+
+ /** Adds a callback to listen to bouncer expansion updates. */
+ fun addBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
+ if (!expansionCallbacks.contains(callback)) {
+ expansionCallbacks.add(callback)
+ }
+ }
+
+ /**
+ * Removes a previously added callback. If the callback was never added, this method does
+ * nothing.
+ */
+ fun removeBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
+ expansionCallbacks.remove(callback)
+ }
+
+ /** Propagate fully shown to bouncer expansion callbacks. */
+ fun dispatchFullyShown() {
+ for (callback in expansionCallbacks) {
+ callback.onFullyShown()
+ }
+ }
+
+ /** Propagate starting to hide to bouncer expansion callbacks. */
+ fun dispatchStartingToHide() {
+ for (callback in expansionCallbacks) {
+ callback.onStartingToHide()
+ }
+ }
+
+ /** Propagate starting to show to bouncer expansion callbacks. */
+ fun dispatchStartingToShow() {
+ for (callback in expansionCallbacks) {
+ callback.onStartingToShow()
+ }
+ }
+
+ /** Propagate fully hidden to bouncer expansion callbacks. */
+ fun dispatchFullyHidden() {
+ for (callback in expansionCallbacks) {
+ callback.onFullyHidden()
+ }
+ }
+
+ /** Propagate expansion changes to bouncer expansion callbacks. */
+ fun dispatchExpansionChanged(expansion: Float) {
+ for (callback in expansionCallbacks) {
+ callback.onExpansionChanged(expansion)
+ }
+ }
+ /** Propagate visibility changes to bouncer expansion callbacks. */
+ fun dispatchVisibilityChanged(visibility: Int) {
+ for (callback in expansionCallbacks) {
+ callback.onVisibilityChanged(visibility == View.VISIBLE)
+ }
+ }
+
+ /** Propagate keyguard reset. */
+ fun dispatchReset() {
+ for (callback in resetCallbacks) {
+ callback.onKeyguardReset()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
new file mode 100644
index 0000000..7d4db37
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.res.ColorStateList
+import android.os.Handler
+import android.os.Trace
+import android.os.UserHandle
+import android.os.UserManager
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.DejankUtils
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic for interacting with the lock-screen bouncer. */
+@SysUISingleton
+class BouncerInteractor
+@Inject
+constructor(
+ private val repository: KeyguardBouncerRepository,
+ private val bouncerView: BouncerView,
+ @Main private val mainHandler: Handler,
+ private val keyguardStateController: KeyguardStateController,
+ private val keyguardSecurityModel: KeyguardSecurityModel,
+ private val callbackInteractor: BouncerCallbackInteractor,
+ private val falsingCollector: FalsingCollector,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
+ keyguardBypassController: KeyguardBypassController,
+ keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+ /** Whether we want to wait for face auth. */
+ private val bouncerFaceDelay =
+ keyguardStateController.isFaceAuthEnabled &&
+ !keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ KeyguardUpdateMonitor.getCurrentUser()
+ ) &&
+ !needsFullscreenBouncer() &&
+ !keyguardUpdateMonitor.userNeedsStrongAuth() &&
+ !keyguardBypassController.bypassEnabled
+
+ /** Runnable to show the bouncer. */
+ val showRunnable = Runnable {
+ repository.setVisible(true)
+ repository.setShow(
+ KeyguardBouncerModel(
+ promptReason = repository.bouncerPromptReason ?: 0,
+ errorMessage = repository.bouncerErrorMessage,
+ expansionAmount = repository.expansionAmount.value
+ )
+ )
+ repository.setShowingSoon(false)
+ }
+
+ val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull()
+ val screenTurnedOff: Flow<Unit> = repository.onScreenTurnedOff.filter { it }.map {}
+ val show: Flow<KeyguardBouncerModel> = repository.show.filterNotNull()
+ val hide: Flow<Unit> = repository.hide.filter { it }.map {}
+ val startingToHide: Flow<Unit> = repository.startingToHide.filter { it }.map {}
+ val isVisible: Flow<Boolean> = repository.isVisible
+ val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
+ val expansionAmount: Flow<Float> = repository.expansionAmount
+ val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
+ val startingDisappearAnimation: Flow<Runnable> =
+ repository.startingDisappearAnimation.filterNotNull()
+ val onDismissAction: Flow<BouncerCallbackActionsModel> =
+ repository.onDismissAction.filterNotNull()
+ val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
+ val keyguardPosition: Flow<Float> = repository.keyguardPosition
+
+ // TODO(b/243685699): Move isScrimmed logic to data layer.
+ // TODO(b/243695312): Encapsulate all of the show logic for the bouncer.
+ /** Show the bouncer if necessary and set the relevant states. */
+ @JvmOverloads
+ fun show(isScrimmed: Boolean) {
+ // Reset some states as we show the bouncer.
+ repository.setShowMessage(null)
+ repository.setOnScreenTurnedOff(false)
+ repository.setKeyguardAuthenticated(null)
+ repository.setHide(false)
+ repository.setStartingToHide(false)
+
+ val resumeBouncer =
+ (repository.isVisible.value || repository.showingSoon.value) && needsFullscreenBouncer()
+
+ if (!resumeBouncer && repository.show.value != null) {
+ // If bouncer is visible, the bouncer is already showing.
+ return
+ }
+
+ val keyguardUserId = KeyguardUpdateMonitor.getCurrentUser()
+ if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
+ // In split system user mode, we never unlock system user.
+ return
+ }
+
+ Trace.beginSection("KeyguardBouncer#show")
+ repository.setScrimmed(isScrimmed)
+ if (isScrimmed) {
+ setExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
+ }
+
+ if (resumeBouncer) {
+ bouncerView.delegate?.resume()
+ // Bouncer is showing the next security screen and we just need to prompt a resume.
+ return
+ }
+ if (bouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
+ // Keyguard is done.
+ return
+ }
+
+ repository.setShowingSoon(true)
+ if (bouncerFaceDelay) {
+ mainHandler.postDelayed(showRunnable, 1200L)
+ } else {
+ DejankUtils.postAfterTraversal(showRunnable)
+ }
+ keyguardStateController.notifyBouncerShowing(true)
+ callbackInteractor.dispatchStartingToShow()
+
+ Trace.endSection()
+ }
+
+ /** Sets the correct bouncer states to hide the bouncer. */
+ fun hide() {
+ Trace.beginSection("KeyguardBouncer#hide")
+ if (isFullyShowing()) {
+ SysUiStatsLog.write(
+ SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN
+ )
+ dismissCallbackRegistry.notifyDismissCancelled()
+ }
+
+ falsingCollector.onBouncerHidden()
+ keyguardStateController.notifyBouncerShowing(false /* showing */)
+ cancelShowRunnable()
+ repository.setShowingSoon(false)
+ repository.setOnDismissAction(null)
+ repository.setVisible(false)
+ repository.setHide(true)
+ repository.setShow(null)
+ Trace.endSection()
+ }
+
+ /**
+ * Sets the panel expansion which is calculated further upstream. Expansion is from 0f to 1f
+ * where 0f => showing and 1f => hiding
+ */
+ fun setExpansion(expansion: Float) {
+ val oldExpansion = repository.expansionAmount.value
+ val expansionChanged = oldExpansion != expansion
+ if (repository.startingDisappearAnimation.value == null) {
+ repository.setExpansion(expansion)
+ }
+
+ if (
+ expansion == KeyguardBouncer.EXPANSION_VISIBLE &&
+ oldExpansion != KeyguardBouncer.EXPANSION_VISIBLE
+ ) {
+ falsingCollector.onBouncerShown()
+ callbackInteractor.dispatchFullyShown()
+ } else if (
+ expansion == KeyguardBouncer.EXPANSION_HIDDEN &&
+ oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN
+ ) {
+ repository.setVisible(false)
+ repository.setShow(null)
+ falsingCollector.onBouncerHidden()
+ DejankUtils.postAfterTraversal { callbackInteractor.dispatchReset() }
+ callbackInteractor.dispatchFullyHidden()
+ } else if (
+ expansion != KeyguardBouncer.EXPANSION_VISIBLE &&
+ oldExpansion == KeyguardBouncer.EXPANSION_VISIBLE
+ ) {
+ callbackInteractor.dispatchStartingToHide()
+ repository.setStartingToHide(true)
+ }
+ if (expansionChanged) {
+ callbackInteractor.dispatchExpansionChanged(expansion)
+ }
+ }
+
+ /** Set the initial keyguard message to show when bouncer is shown. */
+ fun showMessage(message: String?, colorStateList: ColorStateList?) {
+ repository.setShowMessage(BouncerShowMessageModel(message, colorStateList))
+ }
+
+ /**
+ * Sets actions to the bouncer based on how the bouncer is dismissed. If the bouncer is
+ * unlocked, we will run the onDismissAction. If the bouncer is existed before unlocking, we
+ * call cancelAction.
+ */
+ fun setDismissAction(
+ onDismissAction: ActivityStarter.OnDismissAction?,
+ cancelAction: Runnable?
+ ) {
+ repository.setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction))
+ }
+
+ /** Update the resources of the views. */
+ fun updateResources() {
+ repository.setResourceUpdateRequests(true)
+ }
+
+ /** Tell the bouncer that keyguard is authenticated. */
+ fun notifyKeyguardAuthenticated(strongAuth: Boolean) {
+ repository.setKeyguardAuthenticated(strongAuth)
+ }
+
+ /** Tell the bouncer the screen has turned off. */
+ fun onScreenTurnedOff() {
+ repository.setOnScreenTurnedOff(true)
+ }
+
+ /** Update the position of the bouncer when showing. */
+ fun setKeyguardPosition(position: Float) {
+ repository.setKeyguardPosition(position)
+ }
+
+ /** Notifies that the state change was handled. */
+ fun notifyKeyguardAuthenticatedHandled() {
+ repository.setKeyguardAuthenticated(null)
+ }
+
+ /** Notify that view visibility has changed. */
+ fun notifyBouncerVisibilityHasChanged(visibility: Int) {
+ callbackInteractor.dispatchVisibilityChanged(visibility)
+ }
+
+ /** Notify that the resources have been updated */
+ fun notifyUpdatedResources() {
+ repository.setResourceUpdateRequests(false)
+ }
+
+ /** Set whether back button is enabled when on the bouncer screen. */
+ fun setBackButtonEnabled(enabled: Boolean) {
+ repository.setIsBackButtonEnabled(enabled)
+ }
+
+ /** Tell the bouncer to start the pre hide animation. */
+ fun startDisappearAnimation(runnable: Runnable) {
+ val finishRunnable = Runnable {
+ repository.setStartDisappearAnimation(null)
+ runnable.run()
+ }
+ repository.setStartDisappearAnimation(finishRunnable)
+ }
+
+ /** Returns whether bouncer is fully showing. */
+ fun isFullyShowing(): Boolean {
+ return (repository.showingSoon.value || repository.isVisible.value) &&
+ repository.expansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE &&
+ repository.startingDisappearAnimation.value == null
+ }
+
+ /** Returns whether bouncer is scrimmed. */
+ fun isScrimmed(): Boolean {
+ return repository.isScrimmed.value
+ }
+
+ /** If bouncer expansion is between 0f and 1f non-inclusive. */
+ fun isInTransit(): Boolean {
+ return repository.showingSoon.value ||
+ repository.expansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN &&
+ repository.expansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE
+ }
+
+ /** Return whether bouncer is animating away. */
+ fun isAnimatingAway(): Boolean {
+ return repository.startingDisappearAnimation.value != null
+ }
+
+ /** Return whether bouncer will dismiss with actions */
+ fun willDismissWithAction(): Boolean {
+ return repository.onDismissAction.value?.onDismissAction != null
+ }
+
+ /** Returns whether the bouncer should be full screen. */
+ private fun needsFullscreenBouncer(): Boolean {
+ val mode: KeyguardSecurityModel.SecurityMode =
+ keyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser())
+ return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
+ mode == KeyguardSecurityModel.SecurityMode.SimPuk
+ }
+
+ /** Remove the show runnable from the main handler queue to improve performance. */
+ private fun cancelShowRunnable() {
+ DejankUtils.removeCallbacks(showRunnable)
+ mainHandler.removeCallbacks(showRunnable)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt
new file mode 100644
index 0000000..81cf5b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import com.android.systemui.plugins.ActivityStarter
+
+/** Encapsulates callbacks to be invoked by the bouncer logic. */
+// TODO(b/243683121): Move dismiss logic from view controllers
+data class BouncerCallbackActionsModel(
+ val onDismissAction: ActivityStarter.OnDismissAction?,
+ val cancelAction: Runnable?
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt
new file mode 100644
index 0000000..05cdeaa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.content.res.ColorStateList
+
+/** Show a keyguard message to the bouncer. */
+data class BouncerShowMessageModel(val message: String?, val colorStateList: ColorStateList?)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt
new file mode 100644
index 0000000..ad783da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+/** Models the state of the lock-screen bouncer */
+data class KeyguardBouncerModel(
+ val promptReason: Int = 0,
+ val errorMessage: CharSequence? = null,
+ val expansionAmount: Float = 0f,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
new file mode 100644
index 0000000..df26014
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.view.KeyEvent
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.internal.policy.SystemBarUtils
+import com.android.keyguard.KeyguardHostViewController
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.keyguard.data.BouncerViewDelegate
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
+
+/** Binds the bouncer container to its view model. */
+object KeyguardBouncerViewBinder {
+ @JvmStatic
+ fun bind(
+ view: ViewGroup,
+ viewModel: KeyguardBouncerViewModel,
+ componentFactory: KeyguardBouncerComponent.Factory
+ ) {
+ // Builds the KeyguardHostViewController from bouncer view group.
+ val hostViewController: KeyguardHostViewController =
+ componentFactory.create(view).keyguardHostViewController
+ hostViewController.init()
+ val delegate =
+ object : BouncerViewDelegate {
+ override fun isFullScreenBouncer(): Boolean {
+ val mode = hostViewController.currentSecurityMode
+ return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
+ mode == KeyguardSecurityModel.SecurityMode.SimPuk
+ }
+
+ override fun shouldDismissOnMenuPressed(): Boolean {
+ return hostViewController.shouldEnableMenuKey()
+ }
+
+ override fun interceptMediaKey(event: KeyEvent?): Boolean {
+ return hostViewController.interceptMediaKey(event)
+ }
+
+ override fun dispatchBackKeyEventPreIme(): Boolean {
+ return hostViewController.dispatchBackKeyEventPreIme()
+ }
+
+ override fun showNextSecurityScreenOrFinish(): Boolean {
+ return hostViewController.dismiss(KeyguardUpdateMonitor.getCurrentUser())
+ }
+
+ override fun resume() {
+ hostViewController.showPrimarySecurityScreen()
+ hostViewController.onResume()
+ }
+ }
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ try {
+ viewModel.setBouncerViewDelegate(delegate)
+ launch {
+ viewModel.show.collect {
+ hostViewController.showPrimarySecurityScreen()
+ hostViewController.appear(
+ SystemBarUtils.getStatusBarHeight(view.context)
+ )
+ }
+ }
+
+ launch {
+ viewModel.showPromptReason.collect { prompt ->
+ hostViewController.showPromptReason(prompt)
+ }
+ }
+
+ launch {
+ viewModel.showBouncerErrorMessage.collect { errorMessage ->
+ hostViewController.showErrorMessage(errorMessage)
+ }
+ }
+
+ launch {
+ viewModel.showWithFullExpansion.collect { model ->
+ hostViewController.resetSecurityContainer()
+ hostViewController.showPromptReason(model.promptReason)
+ hostViewController.onResume()
+ }
+ }
+
+ launch {
+ viewModel.hide.collect {
+ hostViewController.cancelDismissAction()
+ hostViewController.cleanUp()
+ hostViewController.resetSecurityContainer()
+ }
+ }
+
+ launch {
+ viewModel.startingToHide.collect { hostViewController.onStartingToHide() }
+ }
+
+ launch {
+ viewModel.setDismissAction.collect {
+ hostViewController.setOnDismissAction(
+ it.onDismissAction,
+ it.cancelAction
+ )
+ }
+ }
+
+ launch {
+ viewModel.startDisappearAnimation.collect {
+ hostViewController.startDisappearAnimation(it)
+ }
+ }
+
+ launch {
+ viewModel.bouncerExpansionAmount.collect { expansion ->
+ hostViewController.setExpansion(expansion)
+ }
+ }
+
+ launch {
+ viewModel.bouncerExpansionAmount
+ .filter { it == EXPANSION_VISIBLE }
+ .collect {
+ hostViewController.onResume()
+ view.announceForAccessibility(
+ hostViewController.accessibilityTitleForCurrentMode
+ )
+ }
+ }
+
+ launch {
+ viewModel.isBouncerVisible.collect { isVisible ->
+ val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
+ view.visibility = visibility
+ hostViewController.onBouncerVisibilityChanged(visibility)
+ viewModel.notifyBouncerVisibilityHasChanged(visibility)
+ }
+ }
+
+ launch {
+ viewModel.isBouncerVisible
+ .filter { !it }
+ .collect {
+ // Remove existing input for security reasons.
+ hostViewController.resetSecurityContainer()
+ }
+ }
+
+ launch {
+ viewModel.keyguardPosition.collect { position ->
+ hostViewController.updateKeyguardPosition(position)
+ }
+ }
+
+ launch {
+ viewModel.updateResources.collect {
+ hostViewController.updateResources()
+ viewModel.notifyUpdateResources()
+ }
+ }
+
+ launch {
+ viewModel.bouncerShowMessage.collect {
+ hostViewController.showMessage(it.message, it.colorStateList)
+ }
+ }
+
+ launch {
+ viewModel.keyguardAuthenticated.collect {
+ hostViewController.finish(it, KeyguardUpdateMonitor.getCurrentUser())
+ viewModel.notifyKeyguardAuthenticated()
+ }
+ }
+
+ launch {
+ viewModel
+ .observeOnIsBackButtonEnabled { view.systemUiVisibility }
+ .collect { view.systemUiVisibility = it }
+ }
+
+ launch {
+ viewModel.screenTurnedOff.collect {
+ if (view.visibility == View.VISIBLE) {
+ hostViewController.onPause()
+ }
+ }
+ }
+ awaitCancellation()
+ } finally {
+ viewModel.setBouncerViewDelegate(null)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
new file mode 100644
index 0000000..9ad5211
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.view.View
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.BouncerViewDelegate
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+/** Models UI state for the lock screen bouncer; handles user input. */
+class KeyguardBouncerViewModel
+@Inject
+constructor(
+ private val view: BouncerView,
+ private val interactor: BouncerInteractor,
+) {
+ /** Observe on bouncer expansion amount. */
+ val bouncerExpansionAmount: Flow<Float> = interactor.expansionAmount
+
+ /** Observe on bouncer visibility. */
+ val isBouncerVisible: Flow<Boolean> = interactor.isVisible
+
+ /** Observe whether bouncer is showing. */
+ val show: Flow<KeyguardBouncerModel> = interactor.show
+
+ /** Observe bouncer prompt when bouncer is showing. */
+ val showPromptReason: Flow<Int> = interactor.show.map { it.promptReason }
+
+ /** Observe bouncer error message when bouncer is showing. */
+ val showBouncerErrorMessage: Flow<CharSequence> =
+ interactor.show.map { it.errorMessage }.filterNotNull()
+
+ /** Observe visible expansion when bouncer is showing. */
+ val showWithFullExpansion: Flow<KeyguardBouncerModel> =
+ interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE }
+
+ /** Observe whether bouncer is hiding. */
+ val hide: Flow<Unit> = interactor.hide
+
+ /** Observe whether bouncer is starting to hide. */
+ val startingToHide: Flow<Unit> = interactor.startingToHide
+
+ /** Observe whether we want to set the dismiss action to the bouncer. */
+ val setDismissAction: Flow<BouncerCallbackActionsModel> = interactor.onDismissAction
+
+ /** Observe whether we want to start the disappear animation. */
+ val startDisappearAnimation: Flow<Runnable> = interactor.startingDisappearAnimation
+
+ /** Observe whether we want to update keyguard position. */
+ val keyguardPosition: Flow<Float> = interactor.keyguardPosition
+
+ /** Observe whether we want to update resources. */
+ val updateResources: Flow<Boolean> = interactor.resourceUpdateRequests
+
+ /** Observe whether we want to set a keyguard message when the bouncer shows. */
+ val bouncerShowMessage: Flow<BouncerShowMessageModel> = interactor.showMessage
+
+ /** Observe whether keyguard is authenticated already. */
+ val keyguardAuthenticated: Flow<Boolean> = interactor.keyguardAuthenticated
+
+ /** Observe whether screen is turned off. */
+ val screenTurnedOff: Flow<Unit> = interactor.screenTurnedOff
+
+ /** Notify that view visibility has changed. */
+ fun notifyBouncerVisibilityHasChanged(visibility: Int) {
+ return interactor.notifyBouncerVisibilityHasChanged(visibility)
+ }
+ /** Observe whether we want to update resources. */
+ fun notifyUpdateResources() {
+ interactor.notifyUpdatedResources()
+ }
+
+ /** Notify that keyguard authenticated was handled */
+ fun notifyKeyguardAuthenticated() {
+ interactor.notifyKeyguardAuthenticatedHandled()
+ }
+
+ /** Observe whether back button is enabled. */
+ fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow<Int> {
+ return interactor.isBackButtonEnabled.map { enabled ->
+ var vis: Int = systemUiVisibility()
+ vis =
+ if (enabled) {
+ vis and View.STATUS_BAR_DISABLE_BACK.inv()
+ } else {
+ vis or View.STATUS_BAR_DISABLE_BACK
+ }
+ vis
+ }
+ }
+
+ /** Set an abstraction that will hold reference to the ui delegate for the bouncer view. */
+ fun setBouncerViewDelegate(delegate: BouncerViewDelegate?) {
+ view.delegate = delegate
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index e25f5da..f8c6a57 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media
+import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
@@ -945,6 +946,11 @@
mediaManager.onSwipeToDismiss()
}
+ fun getCurrentVisibleMediaContentIntent(): PendingIntent? {
+ return MediaPlayerData.playerKeys()
+ .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)?.data?.clickIntent
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.apply {
println("keysNeedRemoval: $keysNeedRemoval")
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index dc1488e..53b4d43 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -16,7 +16,7 @@
package com.android.systemui.media.dream;
-import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION;
+import static com.android.systemui.flags.Flags.DREAM_MEDIA_COMPLICATION;
import android.content.Context;
import android.util.Log;
@@ -77,7 +77,7 @@
public void onMediaDataLoaded(@NonNull String key, @Nullable String oldKey,
@NonNull MediaData data, boolean immediately, int receivedSmartspaceCardLatency,
boolean isSsReactivated) {
- if (!mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)) {
+ if (!mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 0ec4eef..97476b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -16,9 +16,6 @@
package com.android.systemui.qs.tiles;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
-
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
@@ -42,6 +39,7 @@
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.data.source.UserRecord;
@@ -73,7 +71,8 @@
mAdapter.refresh();
}
- public static class Adapter extends UserSwitcherController.BaseUserAdapter
+ /** Provides views for user detail items. */
+ public static class Adapter extends BaseUserSwitcherAdapter
implements OnClickListener {
private final Context mContext;
@@ -137,7 +136,7 @@
v.setActivated(item.isCurrent);
v.setDisabledByAdmin(mController.isDisabledByAdmin(item));
v.setEnabled(item.isSwitchToEnabled);
- v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA);
+ UserSwitcherController.setSelectableAlpha(v);
if (item.isCurrent) {
mCurrentUserView = v;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 8d74a09..6be9bbb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -31,10 +31,15 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.LockIconViewController;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -108,7 +113,10 @@
NotificationShadeWindowController controller,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
AmbientState ambientState,
- PulsingGestureListener pulsingGestureListener
+ PulsingGestureListener pulsingGestureListener,
+ FeatureFlags featureFlags,
+ KeyguardBouncerViewModel keyguardBouncerViewModel,
+ KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory
) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
@@ -130,6 +138,12 @@
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
+ if (featureFlags.isEnabled(Flags.MODERN_BOUNCER)) {
+ KeyguardBouncerViewBinder.bind(
+ mView.findViewById(R.id.keyguard_bouncer_container),
+ keyguardBouncerViewModel,
+ keyguardBouncerComponentFactory);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
deleted file mode 100644
index 4551807..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2016 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.systemui.statusbar;
-
-import android.content.Context;
-import android.content.DialogInterface;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-
-public class UserUtil {
- public static void deleteUserWithPrompt(Context context, int userId,
- UserSwitcherController userSwitcherController) {
- new RemoveUserDialog(context, userId, userSwitcherController).show();
- }
-
- private final static class RemoveUserDialog extends SystemUIDialog implements
- DialogInterface.OnClickListener {
-
- private final int mUserId;
- private final UserSwitcherController mUserSwitcherController;
-
- public RemoveUserDialog(Context context, int userId,
- UserSwitcherController userSwitcherController) {
- super(context);
- setTitle(R.string.user_remove_user_title);
- setMessage(context.getString(R.string.user_remove_user_message));
- setButton(DialogInterface.BUTTON_NEUTRAL,
- context.getString(android.R.string.cancel), this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(R.string.user_remove_user_remove), this);
- setCanceledOnTouchOutside(false);
- mUserId = userId;
- mUserSwitcherController = userSwitcherController;
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (which == BUTTON_NEUTRAL) {
- cancel();
- } else {
- dismiss();
- mUserSwitcherController.removeUserId(mUserId);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index d380e9f..d61c51e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -56,7 +56,9 @@
/**
* A class which manages the bouncer on the lockscreen.
+ * @deprecated Use KeyguardBouncerRepository
*/
+@Deprecated
public class KeyguardBouncer {
private static final String TAG = "KeyguardBouncer";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index 4d61689..00c3e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -33,6 +33,7 @@
import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.user.UserSwitchDialogController;
+import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.UserSwitcherActivity;
import com.android.systemui.util.ViewController;
@@ -49,7 +50,7 @@
private final ActivityStarter mActivityStarter;
private final FeatureFlags mFeatureFlags;
- private UserSwitcherController.BaseUserAdapter mUserListener;
+ private BaseUserSwitcherAdapter mUserListener;
private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
@@ -135,7 +136,7 @@
final UserSwitcherController controller = mUserSwitcherController;
if (controller != null) {
- mUserListener = new UserSwitcherController.BaseUserAdapter(controller) {
+ mUserListener = new BaseUserSwitcherAdapter(controller) {
@Override
public void notifyDataSetChanged() {
mView.refreshContentDescription(getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7867147..e61794b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -46,6 +46,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
@@ -53,6 +54,12 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.data.BouncerView;
+import com.android.systemui.keyguard.data.BouncerViewDelegate;
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -123,6 +130,9 @@
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
+ private final BouncerCallbackInteractor mBouncerCallbackInteractor;
+ private final BouncerInteractor mBouncerInteractor;
+ private final BouncerViewDelegate mBouncerViewDelegate;
private final Lazy<com.android.systemui.shade.ShadeController> mShadeController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -197,7 +207,7 @@
private View mNotificationContainer;
- protected KeyguardBouncer mBouncer;
+ @Nullable protected KeyguardBouncer mBouncer;
protected boolean mShowing;
protected boolean mOccluded;
protected boolean mRemoteInputActive;
@@ -223,6 +233,7 @@
private int mLastBiometricMode;
private boolean mLastScreenOffAnimationPlaying;
private float mQsExpansion;
+ private boolean mIsModernBouncerEnabled;
private OnDismissAction mAfterKeyguardGoneAction;
private Runnable mKeyguardGoneCancelAction;
@@ -237,6 +248,7 @@
private final DockManager mDockManager;
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
private final LatencyTracker mLatencyTracker;
+ private final KeyguardSecurityModel mKeyguardSecurityModel;
private KeyguardBypassController mBypassController;
@Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor;
@@ -271,7 +283,12 @@
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
Lazy<ShadeController> shadeController,
- LatencyTracker latencyTracker) {
+ LatencyTracker latencyTracker,
+ KeyguardSecurityModel keyguardSecurityModel,
+ FeatureFlags featureFlags,
+ BouncerCallbackInteractor bouncerCallbackInteractor,
+ BouncerInteractor bouncerInteractor,
+ BouncerView bouncerView) {
mContext = context;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
@@ -288,8 +305,13 @@
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
mShadeController = shadeController;
mLatencyTracker = latencyTracker;
+ mKeyguardSecurityModel = keyguardSecurityModel;
+ mBouncerCallbackInteractor = bouncerCallbackInteractor;
+ mBouncerInteractor = bouncerInteractor;
+ mBouncerViewDelegate = bouncerView.getDelegate();
mFoldAodAnimationController = sysUIUnfoldComponent
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
+ mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER);
}
@Override
@@ -303,7 +325,11 @@
mBiometricUnlockController = biometricUnlockController;
ViewGroup container = mCentralSurfaces.getBouncerContainer();
- mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
+ if (mIsModernBouncerEnabled) {
+ mBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
+ } else {
+ mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
+ }
mNotificationPanelViewController = notificationPanelViewController;
if (panelExpansionStateManager != null) {
panelExpansionStateManager.addExpansionListener(this);
@@ -377,29 +403,45 @@
if (mDozing && !mPulsing) {
return;
} else if (mNotificationPanelViewController.isUnlockHintRunning()) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ if (mBouncer != null) {
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ }
+ mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
// CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
return;
} else if (bouncerNeedsScrimming()) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ if (mBouncer != null) {
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ }
+ mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
&& !mCentralSurfaces.isInLaunchTransition()
&& !isUnlockCollapsing()) {
- mBouncer.setExpansion(fraction);
+ if (mBouncer != null) {
+ mBouncer.setExpansion(fraction);
+ }
+ mBouncerInteractor.setExpansion(fraction);
}
if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
&& !mKeyguardStateController.canDismissLockScreen()
- && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
- mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
+ && !bouncerIsShowing()
+ && !bouncerIsAnimatingAway()) {
+ if (mBouncer != null) {
+ mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
+ }
+ mBouncerInteractor.show(/* isScrimmed= */false);
}
- } else if (!mShowing && mBouncer.inTransit()) {
+ } else if (!mShowing && isBouncerInTransit()) {
// Keyguard is not visible anymore, but expansion animation was still running.
// We need to hide the bouncer, otherwise it will be stuck in transit.
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ if (mBouncer != null) {
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ }
+ mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
@@ -440,15 +482,20 @@
* {@link KeyguardBouncer#needsFullscreenBouncer()}.
*/
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
- if (mBouncer.needsFullscreenBouncer() && !mDozing) {
+ if (needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
mCentralSurfaces.hideKeyguard();
- mBouncer.show(true /* resetSecuritySelection */);
+ if (mBouncer != null) {
+ mBouncer.show(true /* resetSecuritySelection */);
+ }
+ mBouncerInteractor.show(true);
} else {
mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
hideBouncer(false /* destroyView */);
- mBouncer.prepare();
+ if (mBouncer != null) {
+ mBouncer.prepare();
+ }
}
}
updateStates();
@@ -480,10 +527,10 @@
*/
@VisibleForTesting
void hideBouncer(boolean destroyView) {
- if (mBouncer == null) {
- return;
+ if (mBouncer != null) {
+ mBouncer.hide(destroyView);
}
- mBouncer.hide(destroyView);
+ mBouncerInteractor.hide();
if (mShowing) {
// If we were showing the bouncer and then aborting, we need to also clear out any
// potential actions unless we actually unlocked.
@@ -501,8 +548,11 @@
public void showBouncer(boolean scrimmed) {
resetAlternateAuth(false);
- if (mShowing && !mBouncer.isShowing()) {
- mBouncer.show(false /* resetSecuritySelection */, scrimmed);
+ if (mShowing && !isBouncerShowing()) {
+ if (mBouncer != null) {
+ mBouncer.show(false /* resetSecuritySelection */, scrimmed);
+ }
+ mBouncerInteractor.show(scrimmed);
}
updateStates();
}
@@ -535,7 +585,11 @@
// instead of the bouncer.
if (shouldShowAltAuth()) {
if (!afterKeyguardGone) {
- mBouncer.setDismissAction(mAfterKeyguardGoneAction,
+ if (mBouncer != null) {
+ mBouncer.setDismissAction(mAfterKeyguardGoneAction,
+ mKeyguardGoneCancelAction);
+ }
+ mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
mKeyguardGoneCancelAction);
mAfterKeyguardGoneAction = null;
mKeyguardGoneCancelAction = null;
@@ -549,12 +603,18 @@
if (afterKeyguardGone) {
// we'll handle the dismiss action after keyguard is gone, so just show the
// bouncer
- mBouncer.show(false /* resetSecuritySelection */);
+ mBouncerInteractor.show(/* isScrimmed= */true);
+ if (mBouncer != null) mBouncer.show(false /* resetSecuritySelection */);
} else {
// after authentication success, run dismiss action with the option to defer
// hiding the keyguard based on the return value of the OnDismissAction
- mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
- mKeyguardGoneCancelAction);
+ mBouncerInteractor.setDismissAction(
+ mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
+ mBouncerInteractor.show(/* isScrimmed= */true);
+ if (mBouncer != null) {
+ mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
+ mKeyguardGoneCancelAction);
+ }
// bouncer will handle the dismiss action, so we no longer need to track it here
mAfterKeyguardGoneAction = null;
mKeyguardGoneCancelAction = null;
@@ -591,7 +651,7 @@
// Hide bouncer and quick-quick settings.
if (mOccluded && !mDozing) {
mCentralSurfaces.hideKeyguard();
- if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
+ if (hideBouncerWhenShowing || needsFullscreenBouncer()) {
hideBouncer(false /* destroyView */);
}
} else {
@@ -655,7 +715,10 @@
@Override
public void onFinishedGoingToSleep() {
- mBouncer.onScreenTurnedOff();
+ if (mBouncer != null) {
+ mBouncer.onScreenTurnedOff();
+ }
+ mBouncerInteractor.onScreenTurnedOff();
}
@Override
@@ -746,7 +809,7 @@
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
}
- if (animate && !mOccluded && mShowing && !mBouncer.isShowing()) {
+ if (animate && !mOccluded && mShowing && !bouncerIsShowing()) {
mCentralSurfaces.animateKeyguardUnoccluding();
}
}
@@ -762,8 +825,11 @@
@Override
public void startPreHideAnimation(Runnable finishRunnable) {
- if (mBouncer.isShowing()) {
- mBouncer.startPreHideAnimation(finishRunnable);
+ if (bouncerIsShowing()) {
+ if (mBouncer != null) {
+ mBouncer.startPreHideAnimation(finishRunnable);
+ }
+ mBouncerInteractor.startDisappearAnimation(finishRunnable);
mCentralSurfaces.onBouncerPreHideAnimation();
// We update the state (which will show the keyguard) only if an animation will run on
@@ -873,8 +939,12 @@
}
public void onThemeChanged() {
- boolean wasShowing = mBouncer.isShowing();
- boolean wasScrimmed = mBouncer.isScrimmed();
+ if (mIsModernBouncerEnabled) {
+ updateResources();
+ return;
+ }
+ boolean wasShowing = bouncerIsShowing();
+ boolean wasScrimmed = bouncerIsScrimmed();
hideBouncer(true /* destroyView */);
mBouncer.prepare();
@@ -924,7 +994,12 @@
* WARNING: This method might cause Binder calls.
*/
public boolean isSecure() {
- return mBouncer.isSecure();
+ if (mBouncer != null) {
+ return mBouncer.isSecure();
+ }
+
+ return mKeyguardSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
}
@Override
@@ -941,10 +1016,11 @@
* @return whether the back press has been handled
*/
public boolean onBackPressed(boolean hideImmediately) {
- if (mBouncer.isShowing()) {
+ if (bouncerIsShowing()) {
mCentralSurfaces.endAffordanceLaunch();
// The second condition is for SIM card locked bouncer
- if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) {
+ if (bouncerIsScrimmed()
+ && !needsFullscreenBouncer()) {
hideBouncer(false);
updateStates();
} else {
@@ -957,16 +1033,19 @@
@Override
public boolean isBouncerShowing() {
- return mBouncer.isShowing() || isShowingAlternateAuth();
+ return bouncerIsShowing() || isShowingAlternateAuth();
}
@Override
public boolean bouncerIsOrWillBeShowing() {
- return isBouncerShowing() || mBouncer.inTransit();
+ return isBouncerShowing() || isBouncerInTransit();
}
public boolean isFullscreenBouncer() {
- return mBouncer.isFullscreenBouncer();
+ if (mBouncerViewDelegate != null) {
+ return mBouncerViewDelegate.isFullScreenBouncer();
+ }
+ return mBouncer != null && mBouncer.isFullscreenBouncer();
}
/**
@@ -987,7 +1066,7 @@
private long getNavBarShowDelay() {
if (mKeyguardStateController.isKeyguardFadingAway()) {
return mKeyguardStateController.getKeyguardFadingAwayDelay();
- } else if (mBouncer.isShowing()) {
+ } else if (isBouncerShowing()) {
return NAV_BAR_SHOW_DELAY_BOUNCER;
} else {
// No longer dozing, or remote input is active. No delay.
@@ -1010,18 +1089,24 @@
protected void updateStates() {
boolean showing = mShowing;
boolean occluded = mOccluded;
- boolean bouncerShowing = mBouncer.isShowing();
+ boolean bouncerShowing = bouncerIsShowing();
boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing();
- boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
+ boolean bouncerDismissible = !isFullscreenBouncer();
boolean remoteInputActive = mRemoteInputActive;
if ((bouncerDismissible || !showing || remoteInputActive) !=
(mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|| mFirstUpdate) {
if (bouncerDismissible || !showing || remoteInputActive) {
- mBouncer.setBackButtonEnabled(true);
+ if (mBouncer != null) {
+ mBouncer.setBackButtonEnabled(true);
+ }
+ mBouncerInteractor.setBackButtonEnabled(true);
} else {
- mBouncer.setBackButtonEnabled(false);
+ if (mBouncer != null) {
+ mBouncer.setBackButtonEnabled(false);
+ }
+ mBouncerInteractor.setBackButtonEnabled(false);
}
}
@@ -1098,7 +1183,9 @@
|| mPulsing && !mIsDocked)
&& mGesturalNav;
return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying
- || mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav
+ || bouncerIsShowing()
+ || mRemoteInputActive
+ || keyguardWithGestureNav
|| mGlobalActionsVisible);
}
@@ -1117,18 +1204,27 @@
}
public boolean shouldDismissOnMenuPressed() {
- return mBouncer.shouldDismissOnMenuPressed();
+ if (mBouncerViewDelegate != null) {
+ return mBouncerViewDelegate.shouldDismissOnMenuPressed();
+ }
+ return mBouncer != null && mBouncer.shouldDismissOnMenuPressed();
}
public boolean interceptMediaKey(KeyEvent event) {
- return mBouncer.interceptMediaKey(event);
+ if (mBouncerViewDelegate != null) {
+ return mBouncerViewDelegate.interceptMediaKey(event);
+ }
+ return mBouncer != null && mBouncer.interceptMediaKey(event);
}
/**
* @return true if the pre IME back event should be handled
*/
public boolean dispatchBackKeyEventPreIme() {
- return mBouncer.dispatchBackKeyEventPreIme();
+ if (mBouncerViewDelegate != null) {
+ return mBouncerViewDelegate.dispatchBackKeyEventPreIme();
+ }
+ return mBouncer != null && mBouncer.dispatchBackKeyEventPreIme();
}
public void readyForKeyguardDone() {
@@ -1151,7 +1247,7 @@
}
public boolean isSecure(int userId) {
- return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
+ return isSecure() || mLockPatternUtils.isSecure(userId);
}
@Override
@@ -1174,7 +1270,10 @@
* fingerprint.
*/
public void notifyKeyguardAuthenticated(boolean strongAuth) {
- mBouncer.notifyKeyguardAuthenticated(strongAuth);
+ if (mBouncer != null) {
+ mBouncer.notifyKeyguardAuthenticated(strongAuth);
+ }
+ mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) {
resetAlternateAuth(false);
@@ -1189,7 +1288,10 @@
mKeyguardMessageAreaController.setMessage(message);
}
} else {
- mBouncer.showMessage(message, colorState);
+ if (mBouncer != null) {
+ mBouncer.showMessage(message, colorState);
+ }
+ mBouncerInteractor.showMessage(message, colorState);
}
}
@@ -1222,9 +1324,10 @@
public boolean bouncerNeedsScrimming() {
// When a dream overlay is active, scrimming will cause any expansion to immediately expand.
return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
- || mBouncer.willDismissWithAction()
- || (mBouncer.isShowing() && mBouncer.isScrimmed())
- || mBouncer.isFullscreenBouncer();
+ || bouncerWillDismissWithAction()
+ || (bouncerIsShowing()
+ && bouncerIsScrimmed())
+ || isFullscreenBouncer();
}
/**
@@ -1236,6 +1339,7 @@
if (mBouncer != null) {
mBouncer.updateResources();
}
+ mBouncerInteractor.updateResources();
}
public void dump(PrintWriter pw) {
@@ -1289,6 +1393,7 @@
}
}
+ @Nullable
public KeyguardBouncer getBouncer() {
return mBouncer;
}
@@ -1320,6 +1425,8 @@
if (mBouncer != null) {
mBouncer.updateKeyguardPosition(x);
}
+
+ mBouncerInteractor.setKeyguardPosition(x);
}
private static class DismissWithActionRequest {
@@ -1359,9 +1466,65 @@
* Returns if bouncer expansion is between 0 and 1 non-inclusive.
*/
public boolean isBouncerInTransit() {
- if (mBouncer == null) return false;
+ if (mBouncer != null) {
+ return mBouncer.inTransit();
+ }
- return mBouncer.inTransit();
+ return mBouncerInteractor.isInTransit();
+ }
+
+ /**
+ * Returns if bouncer is showing
+ */
+ public boolean bouncerIsShowing() {
+ if (mBouncer != null) {
+ return mBouncer.isShowing();
+ }
+
+ return mBouncerInteractor.isFullyShowing();
+ }
+
+ /**
+ * Returns if bouncer is scrimmed
+ */
+ public boolean bouncerIsScrimmed() {
+ if (mBouncer != null) {
+ return mBouncer.isScrimmed();
+ }
+
+ return mBouncerInteractor.isScrimmed();
+ }
+
+ /**
+ * Returns if bouncer is animating away
+ */
+ public boolean bouncerIsAnimatingAway() {
+ if (mBouncer != null) {
+ return mBouncer.isAnimatingAway();
+ }
+
+ return mBouncerInteractor.isAnimatingAway();
+ }
+
+ /**
+ * Returns if bouncer will dismiss with action
+ */
+ public boolean bouncerWillDismissWithAction() {
+ if (mBouncer != null) {
+ return mBouncer.willDismissWithAction();
+ }
+
+ return mBouncerInteractor.willDismissWithAction();
+ }
+
+ /**
+ * Returns if bouncer needs fullscreen bouncer. i.e. sim pin security method
+ */
+ public boolean needsFullscreenBouncer() {
+ KeyguardSecurityModel.SecurityMode mode = mKeyguardSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser());
+ return mode == KeyguardSecurityModel.SecurityMode.SimPin
+ || mode == KeyguardSecurityModel.SecurityMode.SimPuk;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
new file mode 100644
index 0000000..5b2d695
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.android.systemui.statusbar.policy
+
+import android.content.Context
+import android.graphics.ColorFilter
+import android.graphics.ColorMatrix
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.widget.BaseAdapter
+import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper.getUserRecordName
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper.getUserSwitcherActionIconResourceId
+import java.lang.ref.WeakReference
+
+/** Provides views for user switcher experiences. */
+abstract class BaseUserSwitcherAdapter
+protected constructor(
+ protected val controller: UserSwitcherController,
+) : BaseAdapter() {
+
+ protected open val users: ArrayList<UserRecord>
+ get() = controller.users
+
+ init {
+ controller.addAdapter(WeakReference(this))
+ }
+
+ override fun getCount(): Int {
+ return if (controller.isKeyguardShowing) {
+ users.count { !it.isRestricted }
+ } else {
+ users.size
+ }
+ }
+
+ override fun getItem(position: Int): UserRecord {
+ return users[position]
+ }
+
+ override fun getItemId(position: Int): Long {
+ return position.toLong()
+ }
+
+ /**
+ * Notifies that a user item in the UI has been clicked.
+ *
+ * If the user switcher is hosted in a dialog, passing a non-null [dialogShower] will allow
+ * animation to and from the parent dialog.
+ */
+ @JvmOverloads
+ fun onUserListItemClicked(
+ record: UserRecord,
+ dialogShower: DialogShower? = null,
+ ) {
+ controller.onUserListItemClicked(record, dialogShower)
+ }
+
+ open fun getName(context: Context, item: UserRecord): String {
+ return getName(context, item, false)
+ }
+
+ /** Returns the name for the given {@link UserRecord}. */
+ open fun getName(context: Context, item: UserRecord, isTablet: Boolean): String {
+ return getUserRecordName(
+ context = context,
+ record = item,
+ isGuestUserAutoCreated = controller.isGuestUserAutoCreated,
+ isGuestUserResetting = controller.isGuestUserResetting,
+ isTablet = isTablet,
+ )
+ }
+
+ fun refresh() {
+ controller.refreshUsers(UserHandle.USER_NULL)
+ }
+
+ companion object {
+ @JvmStatic
+ protected val disabledUserAvatarColorFilter: ColorFilter by lazy {
+ val matrix = ColorMatrix()
+ matrix.setSaturation(0f) // 0 - grayscale
+ ColorMatrixColorFilter(matrix)
+ }
+
+ @JvmStatic
+ @JvmOverloads
+ protected fun getIconDrawable(
+ context: Context,
+ item: UserRecord,
+ isTablet: Boolean = false,
+ ): Drawable {
+ val iconRes =
+ getUserSwitcherActionIconResourceId(
+ item.isAddUser,
+ item.isGuest,
+ item.isAddSupervisedUser,
+ isTablet,
+ )
+ return checkNotNull(context.getDrawable(iconRes))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 16306081..dc73d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -69,7 +69,7 @@
private final Context mContext;
private Resources mResources;
private final UserSwitcherController mUserSwitcherController;
- private UserSwitcherController.BaseUserAdapter mAdapter;
+ private BaseUserSwitcherAdapter mAdapter;
private final KeyguardStateController mKeyguardStateController;
private final FalsingManager mFalsingManager;
protected final SysuiStatusBarStateController mStatusBarStateController;
@@ -171,7 +171,7 @@
mUserAvatarView = mView.findViewById(R.id.kg_multi_user_avatar);
mUserAvatarViewWithBackground = mView.findViewById(
R.id.kg_multi_user_avatar_with_background);
- mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) {
+ mAdapter = new BaseUserSwitcherAdapter(mUserSwitcherController) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index e2f5734..0995a00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -232,14 +229,8 @@
}
/**
- * See:
+ * Returns {@code true} if the user switcher should be open by default on the lock screen.
*
- * <ul>
- * <li>{@link com.android.internal.R.bool.config_expandLockScreenUserSwitcher}</li>
- * <li>{@link UserSwitcherController.SIMPLE_USER_SWITCHER_GLOBAL_SETTING}</li>
- * </ul>
- *
- * @return true if the user switcher should be open by default on the lock screen.
* @see android.os.UserManager#isUserSwitcherEnabled()
*/
public boolean isSimpleUserSwitcher() {
@@ -436,7 +427,7 @@
}
static class KeyguardUserAdapter extends
- UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
+ BaseUserSwitcherAdapter implements View.OnClickListener {
private final Context mContext;
private final Resources mResources;
@@ -514,9 +505,9 @@
v.bind(name, drawable, item.info.id);
}
v.setActivated(item.isCurrent);
- v.setDisabledByAdmin(mController.isDisabledByAdmin(item));
+ v.setDisabledByAdmin(getController().isDisabledByAdmin(item));
v.setEnabled(item.isSwitchToEnabled);
- v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA);
+ UserSwitcherController.setSelectableAlpha(v);
if (item.isCurrent) {
mCurrentUserView = v;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
new file mode 100644
index 0000000..843c232
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.android.systemui.statusbar.policy
+
+import android.annotation.UserIdInt
+import android.content.Intent
+import android.view.View
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
+import com.android.systemui.Dumpable
+import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
+import java.lang.ref.WeakReference
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface for a class that provides user switching functionality and state. */
+interface UserSwitcherController : Dumpable {
+
+ /** The current list of [UserRecord]. */
+ val users: ArrayList<UserRecord>
+
+ /** Whether the user switcher experience should use the simple experience. */
+ val isSimpleUserSwitcher: Boolean
+
+ /** Require a view for jank detection */
+ fun init(view: View)
+
+ /** The [UserRecord] of the current user or `null` when none. */
+ val currentUserRecord: UserRecord?
+
+ /** The name of the current user of the device or `null`, when none is selected. */
+ val currentUserName: String?
+
+ /**
+ * Notifies that a user has been selected.
+ *
+ * This will trigger the right user journeys to create a guest user, switch users, and/or
+ * navigate to the correct destination.
+ *
+ * If a user with the given ID is not found, this method is a no-op.
+ *
+ * @param userId The ID of the user to switch to.
+ * @param dialogShower An optional [DialogShower] in case we need to show dialogs.
+ */
+ fun onUserSelected(userId: Int, dialogShower: DialogShower?)
+
+ /** Whether it is allowed to add users while the device is locked. */
+ val isAddUsersFromLockScreenEnabled: Flow<Boolean>
+
+ /** Whether the guest user is configured to always be present on the device. */
+ val isGuestUserAutoCreated: Boolean
+
+ /** Whether the guest user is currently being reset. */
+ val isGuestUserResetting: Boolean
+
+ /** Creates and switches to the guest user. */
+ fun createAndSwitchToGuestUser(dialogShower: DialogShower?)
+
+ /** Shows the add user dialog. */
+ fun showAddUserDialog(dialogShower: DialogShower?)
+
+ /** Starts an activity to add a supervised user to the device. */
+ fun startSupervisedUserActivity()
+
+ /** Notifies when the display density or font scale has changed. */
+ fun onDensityOrFontScaleChanged()
+
+ /** Registers an adapter to notify when the users change. */
+ fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>)
+
+ /** Notifies the item for a user has been clicked. */
+ fun onUserListItemClicked(record: UserRecord, dialogShower: DialogShower?)
+
+ /**
+ * Removes guest user and switches to target user. The guest must be the current user and its id
+ * must be `guestUserId`.
+ *
+ * If `targetUserId` is `UserHandle.USER_NULL`, then create a new guest user in the foreground,
+ * and immediately switch to it. This is used for wiping the current guest and replacing it with
+ * a new one.
+ *
+ * If `targetUserId` is specified, then remove the guest in the background while switching to
+ * `targetUserId`.
+ *
+ * If device is configured with `config_guestUserAutoCreated`, then after guest user is removed,
+ * a new one is created in the background. This has no effect if `targetUserId` is
+ * `UserHandle.USER_NULL`.
+ *
+ * @param guestUserId id of the guest user to remove
+ * @param targetUserId id of the user to switch to after guest is removed. If
+ * `UserHandle.USER_NULL`, then switch immediately to the newly created guest user.
+ */
+ fun removeGuestUser(@UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int)
+
+ /**
+ * Exits guest user and switches to previous non-guest user. The guest must be the current user.
+ *
+ * @param guestUserId user id of the guest user to exit
+ * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when
+ * target user id is not known
+ * @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest
+ * only if its ephemeral, else keep guest
+ */
+ fun exitGuestUser(
+ @UserIdInt guestUserId: Int,
+ @UserIdInt targetUserId: Int,
+ forceRemoveGuestOnExit: Boolean
+ )
+
+ /**
+ * Guarantee guest is present only if the device is provisioned. Otherwise, create a content
+ * observer to wait until the device is provisioned, then schedule the guest creation.
+ */
+ fun schedulePostBootGuestCreation()
+
+ /** Whether keyguard is showing. */
+ val isKeyguardShowing: Boolean
+
+ /** Returns the [EnforcedAdmin] for the given record, or `null` if there isn't one. */
+ fun getEnforcedAdmin(record: UserRecord): EnforcedAdmin?
+
+ /** Returns `true` if the given record is disabled by the admin; `false` otherwise. */
+ fun isDisabledByAdmin(record: UserRecord): Boolean
+
+ /** Starts an activity with the given [Intent]. */
+ fun startActivity(intent: Intent)
+
+ /**
+ * Refreshes users from UserManager.
+ *
+ * The pictures are only loaded if they have not been loaded yet.
+ *
+ * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
+ */
+ fun refreshUsers(forcePictureLoadForId: Int)
+
+ /** Adds a subscriber to when user switches. */
+ fun addUserSwitchCallback(callback: UserSwitchCallback)
+
+ /** Removes a previously-added subscriber. */
+ fun removeUserSwitchCallback(callback: UserSwitchCallback)
+
+ /** Defines interface for classes that can be called back when the user is switched. */
+ fun interface UserSwitchCallback {
+ /** Notifies that the user has switched. */
+ fun onUserSwitched()
+ }
+
+ companion object {
+ /** Alpha value to apply to a user view in the user switcher when it's selectable. */
+ private const val ENABLED_ALPHA =
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA
+
+ /** Alpha value to apply to a user view in the user switcher when it's not selectable. */
+ private const val DISABLED_ALPHA =
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA
+
+ @JvmStatic
+ fun setSelectableAlpha(view: View) {
+ view.alpha =
+ if (view.isEnabled) {
+ ENABLED_ALPHA
+ } else {
+ DISABLED_ALPHA
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
new file mode 100644
index 0000000..12834f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.content.Intent
+import android.view.View
+import com.android.settingslib.RestrictedLockUtils
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.user.data.source.UserRecord
+import dagger.Lazy
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Implementation of [UserSwitcherController]. */
+class UserSwitcherControllerImpl
+@Inject
+constructor(
+ private val flags: FeatureFlags,
+ @Suppress("DEPRECATION") private val oldImpl: Lazy<UserSwitcherControllerOldImpl>,
+) : UserSwitcherController {
+
+ private val isNewImpl: Boolean
+ get() = flags.isEnabled(Flags.REFACTORED_USER_SWITCHER_CONTROLLER)
+ private val _oldImpl: UserSwitcherControllerOldImpl
+ get() = oldImpl.get()
+
+ private fun notYetImplemented(): Nothing {
+ error("Not yet implemented!")
+ }
+
+ override val users: ArrayList<UserRecord>
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.users
+ }
+
+ override val isSimpleUserSwitcher: Boolean
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isSimpleUserSwitcher
+ }
+
+ override fun init(view: View) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.init(view)
+ }
+ }
+
+ override val currentUserRecord: UserRecord?
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.currentUserRecord
+ }
+
+ override val currentUserName: String?
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.currentUserName
+ }
+
+ override fun onUserSelected(
+ userId: Int,
+ dialogShower: UserSwitchDialogController.DialogShower?
+ ) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.onUserSelected(userId, dialogShower)
+ }
+ }
+
+ override val isAddUsersFromLockScreenEnabled: Flow<Boolean>
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isAddUsersFromLockScreenEnabled
+ }
+
+ override val isGuestUserAutoCreated: Boolean
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isGuestUserAutoCreated
+ }
+
+ override val isGuestUserResetting: Boolean
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isGuestUserResetting
+ }
+
+ override fun createAndSwitchToGuestUser(
+ dialogShower: UserSwitchDialogController.DialogShower?,
+ ) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.createAndSwitchToGuestUser(dialogShower)
+ }
+ }
+
+ override fun showAddUserDialog(dialogShower: UserSwitchDialogController.DialogShower?) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.showAddUserDialog(dialogShower)
+ }
+ }
+
+ override fun startSupervisedUserActivity() {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.startSupervisedUserActivity()
+ }
+ }
+
+ override fun onDensityOrFontScaleChanged() {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.onDensityOrFontScaleChanged()
+ }
+ }
+
+ override fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.addAdapter(adapter)
+ }
+ }
+
+ override fun onUserListItemClicked(
+ record: UserRecord,
+ dialogShower: UserSwitchDialogController.DialogShower?,
+ ) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.onUserListItemClicked(record, dialogShower)
+ }
+ }
+
+ override fun removeGuestUser(guestUserId: Int, targetUserId: Int) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.removeGuestUser(guestUserId, targetUserId)
+ }
+ }
+
+ override fun exitGuestUser(
+ guestUserId: Int,
+ targetUserId: Int,
+ forceRemoveGuestOnExit: Boolean
+ ) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
+ }
+ }
+
+ override fun schedulePostBootGuestCreation() {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.schedulePostBootGuestCreation()
+ }
+ }
+
+ override val isKeyguardShowing: Boolean
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isKeyguardShowing
+ }
+
+ override fun getEnforcedAdmin(record: UserRecord): RestrictedLockUtils.EnforcedAdmin? {
+ return if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.getEnforcedAdmin(record)
+ }
+ }
+
+ override fun isDisabledByAdmin(record: UserRecord): Boolean {
+ return if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isDisabledByAdmin(record)
+ }
+ }
+
+ override fun startActivity(intent: Intent) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.startActivity(intent)
+ }
+ }
+
+ override fun refreshUsers(forcePictureLoadForId: Int) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.refreshUsers(forcePictureLoadForId)
+ }
+ }
+
+ override fun addUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.addUserSwitchCallback(callback)
+ }
+ }
+
+ override fun removeUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.removeUserSwitchCallback(callback)
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.dump(pw, args)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java
index 1d5b88e7..d365aa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java
@@ -11,9 +11,8 @@
* 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
+ * limitations under the License.
*/
-
package com.android.systemui.statusbar.policy;
import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
@@ -34,10 +33,6 @@
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -51,7 +46,6 @@
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.WindowManagerGlobal;
-import android.widget.BaseAdapter;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -63,7 +57,6 @@
import com.android.internal.util.LatencyTracker;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.users.UserCreatingDialog;
-import com.android.systemui.Dumpable;
import com.android.systemui.GuestResetOrExitSessionReceiver;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
@@ -86,7 +79,6 @@
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
import com.android.systemui.user.data.source.UserRecord;
-import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -106,15 +98,14 @@
import kotlinx.coroutines.flow.StateFlowKt;
/**
- * Keeps a list of all users on the device for user switching.
+ * Old implementation. Keeps a list of all users on the device for user switching.
+ *
+ * @deprecated This is the old implementation. Please depend on {@link UserSwitcherController}
+ * instead.
*/
+@Deprecated
@SysUISingleton
-public class UserSwitcherController implements Dumpable {
-
- public static final float USER_SWITCH_ENABLED_ALPHA =
- LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA;
- public static final float USER_SWITCH_DISABLED_ALPHA =
- LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA;
+public class UserSwitcherControllerOldImpl implements UserSwitcherController {
private static final String TAG = "UserSwitcherController";
private static final boolean DEBUG = false;
@@ -123,7 +114,7 @@
private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
- private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000l;
+ private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000L;
private static final String INTERACTION_JANK_ADD_NEW_USER_TAG = "add_new_user";
private static final String INTERACTION_JANK_EXIT_GUEST_MODE_TAG = "exit_guest_mode";
@@ -132,7 +123,7 @@
protected final UserTracker mUserTracker;
protected final UserManager mUserManager;
private final ContentObserver mSettingsObserver;
- private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
+ private final ArrayList<WeakReference<BaseUserSwitcherAdapter>> mAdapters = new ArrayList<>();
@VisibleForTesting
final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
@VisibleForTesting
@@ -158,7 +149,6 @@
@VisibleForTesting
Dialog mAddUserDialog;
private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
- private boolean mResumeUserOnGuestLogout = true;
private boolean mSimpleUserSwitcher;
// When false, there won't be any visual affordance to add a new user from the keyguard even if
// the user is unlocked
@@ -187,7 +177,8 @@
Collections.synchronizedList(new ArrayList<>());
@Inject
- public UserSwitcherController(Context context,
+ public UserSwitcherControllerOldImpl(
+ Context context,
IActivityManager activityManager,
UserManager userManager,
UserTracker userTracker,
@@ -303,16 +294,10 @@
refreshUsers(UserHandle.USER_NULL);
}
- /**
- * Refreshes users from UserManager.
- *
- * The pictures are only loaded if they have not been loaded yet.
- *
- * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
- */
+ @Override
@SuppressWarnings("unchecked")
- private void refreshUsers(int forcePictureLoadForId) {
- if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")");
+ public void refreshUsers(int forcePictureLoadForId) {
+ if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId + ")");
if (forcePictureLoadForId != UserHandle.USER_NULL) {
mForcePictureLoadForUserId.put(forcePictureLoadForId, true);
}
@@ -323,8 +308,8 @@
boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL);
SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
- final int N = mUsers.size();
- for (int i = 0; i < N; i++) {
+ final int userCount = mUsers.size();
+ for (int i = 0; i < userCount; i++) {
UserRecord r = mUsers.get(i);
if (r == null || r.picture == null || r.info == null || forceAllUsers
|| mForcePictureLoadForUserId.get(r.info.id)) {
@@ -431,38 +416,41 @@
});
}
- boolean systemCanCreateUsers() {
+ private boolean systemCanCreateUsers() {
return !mUserManager.hasBaseUserRestriction(
UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
}
- boolean currentUserCanCreateUsers() {
+ private boolean currentUserCanCreateUsers() {
UserInfo currentUser = mUserTracker.getUserInfo();
return currentUser != null
&& (currentUser.isAdmin() || mUserTracker.getUserId() == UserHandle.USER_SYSTEM)
&& systemCanCreateUsers();
}
- boolean anyoneCanCreateUsers() {
+ private boolean anyoneCanCreateUsers() {
return systemCanCreateUsers() && mAddUsersFromLockScreen.getValue();
}
+ @VisibleForTesting
boolean canCreateGuest(boolean hasExistingGuest) {
return mUserSwitcherEnabled
&& (currentUserCanCreateUsers() || anyoneCanCreateUsers())
&& !hasExistingGuest;
}
+ @VisibleForTesting
boolean canCreateUser() {
return mUserSwitcherEnabled
&& (currentUserCanCreateUsers() || anyoneCanCreateUsers())
&& mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
}
- boolean createIsRestricted() {
+ private boolean createIsRestricted() {
return !mAddUsersFromLockScreen.getValue();
}
+ @VisibleForTesting
boolean canCreateSupervisedUser() {
return !TextUtils.isEmpty(mCreateSupervisedUserPackage) && canCreateUser();
}
@@ -476,7 +464,7 @@
private void notifyAdapters() {
for (int i = mAdapters.size() - 1; i >= 0; i--) {
- BaseUserAdapter adapter = mAdapters.get(i).get();
+ BaseUserSwitcherAdapter adapter = mAdapters.get(i).get();
if (adapter != null) {
adapter.notifyDataSetChanged();
} else {
@@ -485,37 +473,20 @@
}
}
+ @Override
public boolean isSimpleUserSwitcher() {
return mSimpleUserSwitcher;
}
- public void setResumeUserOnGuestLogout(boolean resume) {
- mResumeUserOnGuestLogout = resume;
- }
-
/**
* Returns whether the current user is a system user.
*/
- public boolean isSystemUser() {
+ @VisibleForTesting
+ boolean isSystemUser() {
return mUserTracker.getUserId() == UserHandle.USER_SYSTEM;
}
- public void removeUserId(int userId) {
- if (userId == UserHandle.USER_SYSTEM) {
- Log.w(TAG, "User " + userId + " could not removed.");
- return;
- }
- if (mUserTracker.getUserId() == userId) {
- switchToUserId(UserHandle.USER_SYSTEM);
- }
- if (mUserManager.removeUser(userId)) {
- refreshUsers(UserHandle.USER_NULL);
- }
- }
-
- /**
- * @return UserRecord for the current user
- */
+ @Override
public @Nullable UserRecord getCurrentUserRecord() {
for (int i = 0; i < mUsers.size(); ++i) {
UserRecord userRecord = mUsers.get(i);
@@ -526,17 +497,7 @@
return null;
}
- /**
- * Notifies that a user has been selected.
- *
- * <p>This will trigger the right user journeys to create a guest user, switch users, and/or
- * navigate to the correct destination.
- *
- * <p>If a user with the given ID is not found, this method is a no-op.
- *
- * @param userId The ID of the user to switch to.
- * @param dialogShower An optional {@link DialogShower} in case we need to show dialogs.
- */
+ @Override
public void onUserSelected(int userId, @Nullable DialogShower dialogShower) {
UserRecord userRecord = mUsers.stream()
.filter(x -> x.resolveId() == userId)
@@ -549,23 +510,23 @@
onUserListItemClicked(userRecord, dialogShower);
}
- /** Whether it is allowed to add users while the device is locked. */
- public Flow<Boolean> getAddUsersFromLockScreen() {
+ @Override
+ public Flow<Boolean> isAddUsersFromLockScreenEnabled() {
return mAddUsersFromLockScreen;
}
- /** Returns {@code true} if the guest user is configured to always be present on the device. */
+ @Override
public boolean isGuestUserAutoCreated() {
return mGuestUserAutoCreated;
}
- /** Returns {@code true} if the guest user is currently being reset. */
+ @Override
public boolean isGuestUserResetting() {
return mGuestIsResetting.get();
}
- @VisibleForTesting
- void onUserListItemClicked(UserRecord record, DialogShower dialogShower) {
+ @Override
+ public void onUserListItemClicked(UserRecord record, DialogShower dialogShower) {
if (record.isGuest && record.info == null) {
createAndSwitchToGuestUser(dialogShower);
} else if (record.isAddUser) {
@@ -604,7 +565,7 @@
switchToUserId(id);
}
- protected void switchToUserId(int id) {
+ private void switchToUserId(int id) {
try {
if (mView != null) {
mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
@@ -621,7 +582,7 @@
private void showExitGuestDialog(int id, boolean isGuestEphemeral, DialogShower dialogShower) {
int newId = UserHandle.USER_SYSTEM;
- if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
+ if (mLastNonGuestUser != UserHandle.USER_SYSTEM) {
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
newId = info.id;
@@ -645,9 +606,7 @@
}
}
- /**
- * Creates and switches to the guest user.
- */
+ @Override
public void createAndSwitchToGuestUser(@Nullable DialogShower dialogShower) {
createGuestAsync(guestId -> {
// guestId may be USER_NULL if we haven't reloaded the user list yet.
@@ -658,9 +617,7 @@
});
}
- /**
- * Shows the add user dialog.
- */
+ @Override
public void showAddUserDialog(@Nullable DialogShower dialogShower) {
if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
mAddUserDialog.cancel();
@@ -677,9 +634,7 @@
}
}
- /**
- * Starts an activity to add a supervised user to the device.
- */
+ @Override
public void startSupervisedUserActivity() {
final Intent intent = new Intent()
.setAction(UserManager.ACTION_CREATE_SUPERVISED_USER)
@@ -711,7 +666,7 @@
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Log.v(TAG, "Broadcast: a=" + intent.getAction()
- + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
+ + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
}
boolean unpauseRefreshUsers = false;
@@ -725,8 +680,8 @@
final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
final UserInfo userInfo = mUserManager.getUserInfo(currentId);
- final int N = mUsers.size();
- for (int i = 0; i < N; i++) {
+ final int userCount = mUsers.size();
+ for (int i = 0; i < userCount; i++) {
UserRecord record = mUsers.get(i);
if (record.info == null) continue;
boolean shouldBeCurrent = record.info.id == currentId;
@@ -805,7 +760,7 @@
pw.println("mGuestUserAutoCreated=" + mGuestUserAutoCreated);
}
- /** Returns the name of the current user of the phone. */
+ @Override
public String getCurrentUserName() {
if (mUsers.isEmpty()) return null;
UserRecord item = mUsers.stream().filter(x -> x.isCurrent).findFirst().orElse(null);
@@ -814,40 +769,22 @@
return item.info.name;
}
+ @Override
public void onDensityOrFontScaleChanged() {
refreshUsers(UserHandle.USER_ALL);
}
- @VisibleForTesting
- public void addAdapter(WeakReference<BaseUserAdapter> adapter) {
+ @Override
+ public void addAdapter(WeakReference<BaseUserSwitcherAdapter> adapter) {
mAdapters.add(adapter);
}
- @VisibleForTesting
+ @Override
public ArrayList<UserRecord> getUsers() {
return mUsers;
}
- /**
- * Removes guest user and switches to target user. The guest must be the current user and its id
- * must be {@code guestUserId}.
- *
- * <p>If {@code targetUserId} is {@link UserHandle#USER_NULL}, then create a new guest user in
- * the foreground, and immediately switch to it. This is used for wiping the current guest and
- * replacing it with a new one.
- *
- * <p>If {@code targetUserId} is specified, then remove the guest in the background while
- * switching to {@code targetUserId}.
- *
- * <p>If device is configured with {@link
- * com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a
- * new one is created in the background. This has no effect if {@code targetUserId} is {@link
- * UserHandle#USER_NULL}.
- *
- * @param guestUserId id of the guest user to remove
- * @param targetUserId id of the user to switch to after guest is removed. If {@link
- * UserHandle#USER_NULL}, then switch immediately to the newly created guest user.
- */
+ @Override
public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) {
UserInfo currentUser = mUserTracker.getUserInfo();
if (currentUser.id != guestUserId) {
@@ -894,18 +831,9 @@
}
}
- /**
- * Exits guest user and switches to previous non-guest user. The guest must be the current
- * user.
- *
- * @param guestUserId user id of the guest user to exit
- * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when
- * target user id is not known
- * @param forceRemoveGuestOnExit true: remove guest before switching user,
- * false: remove guest only if its ephemeral, else keep guest
- */
+ @Override
public void exitGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId,
- boolean forceRemoveGuestOnExit) {
+ boolean forceRemoveGuestOnExit) {
UserInfo currentUser = mUserTracker.getUserInfo();
if (currentUser.id != guestUserId) {
Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
@@ -921,7 +849,7 @@
int newUserId = UserHandle.USER_SYSTEM;
if (targetUserId == UserHandle.USER_NULL) {
// when target user is not specified switch to last non guest user
- if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
+ if (mLastNonGuestUser != UserHandle.USER_SYSTEM) {
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
newUserId = info.id;
@@ -959,10 +887,7 @@
}
- /**
- * Guarantee guest is present only if the device is provisioned. Otherwise, create a content
- * observer to wait until the device is provisioned, then schedule the guest creation.
- */
+ @Override
public void schedulePostBootGuestCreation() {
if (isDeviceAllowedToAddGuest()) {
guaranteeGuestPresent();
@@ -1014,7 +939,7 @@
* @return The multi-user user ID of the newly created guest user, or
* {@link UserHandle#USER_NULL} if the guest couldn't be created.
*/
- public @UserIdInt int createGuest() {
+ private @UserIdInt int createGuest() {
UserInfo guest;
try {
guest = mUserManager.createGuest(mContext);
@@ -1029,135 +954,27 @@
return guest.id;
}
- /**
- * Require a view for jank detection
- */
+ @Override
public void init(View view) {
mView = view;
}
- @VisibleForTesting
- public KeyguardStateController getKeyguardStateController() {
- return mKeyguardStateController;
+ @Override
+ public boolean isKeyguardShowing() {
+ return mKeyguardStateController.isShowing();
}
- /**
- * Returns the {@link EnforcedAdmin} for the given record, or {@code null} if there isn't one.
- */
+ @Override
@Nullable
public EnforcedAdmin getEnforcedAdmin(UserRecord record) {
return mEnforcedAdminByUserRecord.get(record);
}
- /**
- * Returns {@code true} if the given record is disabled by the admin; {@code false} otherwise.
- */
+ @Override
public boolean isDisabledByAdmin(UserRecord record) {
return mDisabledByAdmin.contains(record);
}
- public static abstract class BaseUserAdapter extends BaseAdapter {
-
- final UserSwitcherController mController;
- private final KeyguardStateController mKeyguardStateController;
-
- protected BaseUserAdapter(UserSwitcherController controller) {
- mController = controller;
- mKeyguardStateController = controller.getKeyguardStateController();
- controller.addAdapter(new WeakReference<>(this));
- }
-
- protected ArrayList<UserRecord> getUsers() {
- return mController.getUsers();
- }
-
- public int getUserCount() {
- return countUsers(false);
- }
-
- @Override
- public int getCount() {
- return countUsers(true);
- }
-
- private int countUsers(boolean includeGuest) {
- boolean keyguardShowing = mKeyguardStateController.isShowing();
- final int userSize = getUsers().size();
- int count = 0;
- for (int i = 0; i < userSize; i++) {
- if (getUsers().get(i).isGuest && !includeGuest) {
- continue;
- }
- if (getUsers().get(i).isRestricted && keyguardShowing) {
- break;
- }
- count++;
- }
- return count;
- }
-
- @Override
- public UserRecord getItem(int position) {
- return getUsers().get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- /**
- * It handles click events on user list items.
- *
- * If the user switcher is hosted in a dialog, passing a non-null {@link DialogShower}
- * will allow animation to and from the parent dialog.
- *
- */
- public void onUserListItemClicked(UserRecord record, @Nullable DialogShower dialogShower) {
- mController.onUserListItemClicked(record, dialogShower);
- }
-
- public void onUserListItemClicked(UserRecord record) {
- onUserListItemClicked(record, null);
- }
-
- public String getName(Context context, UserRecord item) {
- return getName(context, item, false);
- }
-
- /**
- * Returns the name for the given {@link UserRecord}.
- */
- public String getName(Context context, UserRecord item, boolean isTablet) {
- return LegacyUserUiHelper.getUserRecordName(
- context,
- item,
- mController.isGuestUserAutoCreated(),
- mController.isGuestUserResetting(),
- isTablet);
- }
-
- protected static ColorFilter getDisabledUserAvatarColorFilter() {
- ColorMatrix matrix = new ColorMatrix();
- matrix.setSaturation(0f); // 0 - grayscale
- return new ColorMatrixColorFilter(matrix);
- }
-
- protected static Drawable getIconDrawable(Context context, UserRecord item) {
- return getIconDrawable(context, item, false);
- }
- protected static Drawable getIconDrawable(Context context, UserRecord item,
- boolean isTablet) {
- int iconRes = LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
- item.isAddUser, item.isGuest, item.isAddSupervisedUser, isTablet);
- return context.getDrawable(iconRes);
- }
-
- public void refresh() {
- mController.refreshUsers(UserHandle.USER_NULL);
- }
- }
-
private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId());
@@ -1178,20 +995,17 @@
defaultSimpleUserSwitcher, UserHandle.USER_SYSTEM) != 0;
}
+ @Override
public void startActivity(Intent intent) {
mActivityStarter.startActivity(intent, true);
}
- /**
- * Add a subscriber to when user switches.
- */
+ @Override
public void addUserSwitchCallback(UserSwitchCallback callback) {
mUserSwitchCallbacks.add(callback);
}
- /**
- * Remove a subscriber to when user switches.
- */
+ @Override
public void removeUserSwitchCallback(UserSwitchCallback callback) {
mUserSwitchCallbacks.remove(callback);
}
@@ -1218,7 +1032,7 @@
// which
// helps making the transition faster.
if (!mKeyguardStateController.isShowing()) {
- mHandler.post(UserSwitcherController.this::notifyAdapters);
+ mHandler.post(UserSwitcherControllerOldImpl.this::notifyAdapters);
} else {
notifyAdapters();
}
@@ -1367,13 +1181,4 @@
}
}
- /**
- * Callback to for when this controller receives the intent to switch users.
- */
- public interface UserSwitchCallback {
- /**
- * Called when user has switched.
- */
- void onUserSwitched();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 1b73539..b1b45b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -58,6 +58,8 @@
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.UserSwitcherControllerImpl;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.statusbar.policy.WalletControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -196,4 +198,8 @@
static DataSaverController provideDataSaverController(NetworkController networkController) {
return networkController.getDataSaverController();
}
+
+ /** Binds {@link UserSwitcherController} to its implementation. */
+ @Binds
+ UserSwitcherController bindUserSwitcherController(UserSwitcherControllerImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 5e2dde6..108ab43 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -53,10 +53,8 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter
import com.android.systemui.statusbar.policy.UserSwitcherController
-import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter
-import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA
-import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.ui.binder.UserSwitcherViewBinder
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
@@ -66,10 +64,10 @@
private const val USER_VIEW = "user_view"
-/**
- * Support a fullscreen user switcher
- */
-open class UserSwitcherActivity @Inject constructor(
+/** Support a fullscreen user switcher */
+open class UserSwitcherActivity
+@Inject
+constructor(
private val userSwitcherController: UserSwitcherController,
private val broadcastDispatcher: BroadcastDispatcher,
private val falsingCollector: FalsingCollector,
@@ -86,11 +84,12 @@
private lateinit var addButton: View
private var addUserRecords = mutableListOf<UserRecord>()
private val onBackCallback = OnBackInvokedCallback { finish() }
- private val userSwitchedCallback: UserTracker.Callback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- finish()
+ private val userSwitchedCallback: UserTracker.Callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ finish()
+ }
}
- }
// When the add users options become available, insert another option to manage users
private val manageUserRecord =
UserRecord(
@@ -114,13 +113,14 @@
@VisibleForTesting
fun createActivity() {
setContentView(R.layout.user_switcher_fullscreen)
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+ window.decorView.systemUiVisibility =
+ (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
if (isUsingModernArchitecture()) {
Log.d(TAG, "Using modern architecture.")
- val viewModel = ViewModelProvider(
- this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
+ val viewModel =
+ ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
UserSwitcherViewBinder.bind(
view = requireViewById(R.id.user_switcher_root),
viewModel = viewModel,
@@ -136,27 +136,23 @@
parent = requireViewById<UserSwitcherRootView>(R.id.user_switcher_root)
- parent.touchHandler = object : Gefingerpoken {
- override fun onTouchEvent(ev: MotionEvent?): Boolean {
- falsingCollector.onTouchEvent(ev)
- return false
+ parent.touchHandler =
+ object : Gefingerpoken {
+ override fun onTouchEvent(ev: MotionEvent?): Boolean {
+ falsingCollector.onTouchEvent(ev)
+ return false
+ }
}
- }
- requireViewById<View>(R.id.cancel).apply {
- setOnClickListener {
- _ -> finish()
- }
- }
+ requireViewById<View>(R.id.cancel).apply { setOnClickListener { _ -> finish() } }
- addButton = requireViewById<View>(R.id.add).apply {
- setOnClickListener {
- _ -> showPopupMenu()
- }
- }
+ addButton =
+ requireViewById<View>(R.id.add).apply { setOnClickListener { _ -> showPopupMenu() } }
onBackInvokedDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackCallback)
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ onBackCallback
+ )
userSwitcherController.init(parent)
initBroadcastReceiver()
@@ -169,25 +165,30 @@
val items = mutableListOf<UserRecord>()
addUserRecords.forEach { items.add(it) }
- var popupMenuAdapter = ItemAdapter(
- this,
- R.layout.user_switcher_fullscreen_popup_item,
- layoutInflater,
- { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item, true) },
- { item: UserRecord -> adapter.findUserIcon(item, true).mutate().apply {
- setTint(resources.getColor(
- R.color.user_switcher_fullscreen_popup_item_tint,
- getTheme()
- ))
- } }
- )
+ var popupMenuAdapter =
+ ItemAdapter(
+ this,
+ R.layout.user_switcher_fullscreen_popup_item,
+ layoutInflater,
+ { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item, true) },
+ { item: UserRecord ->
+ adapter.findUserIcon(item, true).mutate().apply {
+ setTint(
+ resources.getColor(
+ R.color.user_switcher_fullscreen_popup_item_tint,
+ getTheme()
+ )
+ )
+ }
+ }
+ )
popupMenuAdapter.addAll(items)
- popupMenu = UserSwitcherPopupMenu(this).apply {
- setAnchorView(addButton)
- setAdapter(popupMenuAdapter)
- setOnItemClickListener {
- parent: AdapterView<*>, view: View, pos: Int, id: Long ->
+ popupMenu =
+ UserSwitcherPopupMenu(this).apply {
+ setAnchorView(addButton)
+ setAdapter(popupMenuAdapter)
+ setOnItemClickListener { parent: AdapterView<*>, view: View, pos: Int, id: Long ->
if (falsingManager.isFalseTap(LOW_PENALTY) || !view.isEnabled()) {
return@setOnItemClickListener
}
@@ -206,10 +207,10 @@
if (!item.isAddUser) {
this@UserSwitcherActivity.finish()
}
- }
+ }
- show()
- }
+ show()
+ }
}
private fun buildUserViews() {
@@ -227,8 +228,8 @@
val totalWidth = parent.width
val userViewCount = adapter.getTotalUserViews()
val maxColumns = getMaxColumns(userViewCount)
- val horizontalGap = resources
- .getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap)
+ val horizontalGap =
+ resources.getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap)
val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap
val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns
@@ -299,14 +300,15 @@
}
private fun initBroadcastReceiver() {
- broadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- val action = intent.getAction()
- if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- finish()
+ broadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.getAction()
+ if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ finish()
+ }
}
}
- }
val filter = IntentFilter()
filter.addAction(Intent.ACTION_SCREEN_OFF)
@@ -322,9 +324,7 @@
return flags.isEnabled(Flags.MODERN_USER_SWITCHER_ACTIVITY)
}
- /**
- * Provides views to populate the option menu.
- */
+ /** Provides views to populate the option menu. */
private class ItemAdapter(
val parentContext: Context,
val resource: Int,
@@ -337,43 +337,27 @@
val item = getItem(position)
val view = convertView ?: layoutInflater.inflate(resource, parent, false)
- view.requireViewById<ImageView>(R.id.icon).apply {
- setImageDrawable(iconGetter(item))
- }
- view.requireViewById<TextView>(R.id.text).apply {
- setText(textGetter(item))
- }
+ view.requireViewById<ImageView>(R.id.icon).apply { setImageDrawable(iconGetter(item)) }
+ view.requireViewById<TextView>(R.id.text).apply { setText(textGetter(item)) }
return view
}
}
- private inner class UserAdapter : BaseUserAdapter(userSwitcherController) {
+ private inner class UserAdapter : BaseUserSwitcherAdapter(userSwitcherController) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val item = getItem(position)
var view = convertView as ViewGroup?
if (view == null) {
- view = layoutInflater.inflate(
- R.layout.user_switcher_fullscreen_item,
- parent,
- false
- ) as ViewGroup
+ view =
+ layoutInflater.inflate(R.layout.user_switcher_fullscreen_item, parent, false)
+ as ViewGroup
}
- (view.getChildAt(0) as ImageView).apply {
- setImageDrawable(getDrawable(item))
- }
- (view.getChildAt(1) as TextView).apply {
- setText(getName(getContext(), item))
- }
+ (view.getChildAt(0) as ImageView).apply { setImageDrawable(getDrawable(item)) }
+ (view.getChildAt(1) as TextView).apply { setText(getName(getContext(), item)) }
view.setEnabled(item.isSwitchToEnabled)
- view.setAlpha(
- if (view.isEnabled()) {
- USER_SWITCH_ENABLED_ALPHA
- } else {
- USER_SWITCH_DISABLED_ALPHA
- }
- )
+ UserSwitcherController.setSelectableAlpha(view)
view.setTag(USER_VIEW)
return view
}
@@ -401,23 +385,20 @@
}
fun getTotalUserViews(): Int {
- return users.count { item ->
- !doNotRenderUserView(item)
- }
+ return users.count { item -> !doNotRenderUserView(item) }
}
fun doNotRenderUserView(item: UserRecord): Boolean {
- return item.isAddUser ||
- item.isAddSupervisedUser ||
- item.isGuest && item.info == null
+ return item.isAddUser || item.isAddSupervisedUser || item.isGuest && item.info == null
}
private fun getDrawable(item: UserRecord): Drawable {
- var drawable = if (item.isGuest) {
- getDrawable(R.drawable.ic_account_circle)
- } else {
- findUserIcon(item)
- }
+ var drawable =
+ if (item.isGuest) {
+ getDrawable(R.drawable.ic_account_circle)
+ } else {
+ findUserIcon(item)
+ }
drawable.mutate()
if (!item.isCurrent && !item.isSwitchToEnabled) {
@@ -429,16 +410,16 @@
)
}
- val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate()
- as LayerDrawable
- if (item == userSwitcherController.getCurrentUserRecord()) {
+ val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() as LayerDrawable
+ if (item == userSwitcherController.currentUserRecord) {
(ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
- val stroke = resources
- .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
- val color = Utils.getColorAttrDefaultColor(
- this@UserSwitcherActivity,
- com.android.internal.R.attr.colorAccentPrimary
- )
+ val stroke =
+ resources.getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
+ val color =
+ Utils.getColorAttrDefaultColor(
+ this@UserSwitcherActivity,
+ com.android.internal.R.attr.colorAccentPrimary
+ )
setStroke(stroke, color)
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 305b5ee..0356388 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -99,7 +99,7 @@
override val actions: Flow<List<UserActionModel>> =
userRecords.map { records -> records.filter { it.isNotUser() }.map { it.toActionModel() } }
- override val isActionableWhenLocked: Flow<Boolean> = controller.addUsersFromLockScreen
+ override val isActionableWhenLocked: Flow<Boolean> = controller.isAddUsersFromLockScreenEnabled
override val isGuestUserAutoCreated: Boolean = controller.isGuestUserAutoCreated
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 28e99da..43f6f1a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -116,9 +116,7 @@
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User");
- when(mUserSwitcherController.getKeyguardStateController())
- .thenReturn(mKeyguardStateController);
- when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mUserSwitcherController.isKeyguardShowing()).thenReturn(true);
mScreenWidth = getUiDevice().getDisplayWidth();
mFakeMeasureSpec = View
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index d70467d..c5a7de4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -36,6 +36,7 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
@@ -88,6 +89,9 @@
@Mock
ViewRootImpl mViewRoot;
+ @Mock
+ BouncerCallbackInteractor mBouncerCallbackInteractor;
+
DreamOverlayContainerViewController mController;
@Before
@@ -110,7 +114,8 @@
mResources,
MAX_BURN_IN_OFFSET,
BURN_IN_PROTECTION_UPDATE_INTERVAL,
- MILLIS_UNTIL_FULL_JITTER);
+ MILLIS_UNTIL_FULL_JITTER,
+ mBouncerCallbackInteractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java
index bc94440..522b5b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java
@@ -16,17 +16,28 @@
package com.android.systemui.dreams.complication;
-import static org.mockito.Mockito.verify;
+import static com.android.systemui.flags.Flags.DREAM_MEDIA_TAP_TO_OPEN;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.Intent;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import androidx.test.filters.SmallTest;
+import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -48,21 +59,52 @@
@Mock
private MediaDreamComplication mMediaComplication;
+ @Mock
+ private MediaCarouselController mMediaCarouselController;
+
+ @Mock
+ private ActivityStarter mActivityStarter;
+
+ @Mock
+ private ActivityIntentHelper mActivityIntentHelper;
+
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+
+ @Mock
+ private NotificationLockscreenUserManager mLockscreenUserManager;
+
+ @Mock
+ private FeatureFlags mFeatureFlags;
+
+ @Mock
+ private PendingIntent mPendingIntent;
+
+ private final Intent mIntent = new Intent("android.test.TEST_ACTION");
+ private final Integer mCurrentUserId = 99;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(false);
}
/**
* Ensures clicking media entry chip adds/removes media complication.
*/
@Test
- public void testClick() {
+ public void testClickToOpenUMO() {
final DreamMediaEntryComplication.DreamMediaEntryViewController viewController =
new DreamMediaEntryComplication.DreamMediaEntryViewController(
mView,
mDreamOverlayStateController,
- mMediaComplication);
+ mMediaComplication,
+ mMediaCarouselController,
+ mActivityStarter,
+ mActivityIntentHelper,
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mFeatureFlags);
final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
ArgumentCaptor.forClass(View.OnClickListener.class);
@@ -85,10 +127,90 @@
new DreamMediaEntryComplication.DreamMediaEntryViewController(
mView,
mDreamOverlayStateController,
- mMediaComplication);
+ mMediaComplication,
+ mMediaCarouselController,
+ mActivityStarter,
+ mActivityIntentHelper,
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mFeatureFlags);
viewController.onViewDetached();
verify(mView).setSelected(false);
verify(mDreamOverlayStateController).removeComplication(mMediaComplication);
}
+
+ /**
+ * Ensures clicking media entry chip opens media when flag is set.
+ */
+ @Test
+ public void testClickToOpenMediaOverLockscreen() {
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(true);
+
+ when(mMediaCarouselController.getCurrentVisibleMediaContentIntent()).thenReturn(
+ mPendingIntent);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mLockscreenUserManager.getCurrentUserId()).thenReturn(mCurrentUserId);
+
+ final DreamMediaEntryComplication.DreamMediaEntryViewController viewController =
+ new DreamMediaEntryComplication.DreamMediaEntryViewController(
+ mView,
+ mDreamOverlayStateController,
+ mMediaComplication,
+ mMediaCarouselController,
+ mActivityStarter,
+ mActivityIntentHelper,
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mFeatureFlags);
+ viewController.onViewAttached();
+
+ final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
+ ArgumentCaptor.forClass(View.OnClickListener.class);
+ verify(mView).setOnClickListener(clickListenerCaptor.capture());
+
+ when(mActivityIntentHelper.wouldShowOverLockscreen(mIntent, mCurrentUserId)).thenReturn(
+ true);
+
+ clickListenerCaptor.getValue().onClick(mView);
+ verify(mActivityStarter).startActivity(mIntent, true, null, true);
+ }
+
+ /**
+ * Ensures clicking media entry chip opens media when flag is set.
+ */
+ @Test
+ public void testClickToOpenMediaDismissingLockscreen() {
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(true);
+
+ when(mMediaCarouselController.getCurrentVisibleMediaContentIntent()).thenReturn(
+ mPendingIntent);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mLockscreenUserManager.getCurrentUserId()).thenReturn(mCurrentUserId);
+
+ final DreamMediaEntryComplication.DreamMediaEntryViewController viewController =
+ new DreamMediaEntryComplication.DreamMediaEntryViewController(
+ mView,
+ mDreamOverlayStateController,
+ mMediaComplication,
+ mMediaCarouselController,
+ mActivityStarter,
+ mActivityIntentHelper,
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mFeatureFlags);
+ viewController.onViewAttached();
+
+ final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
+ ArgumentCaptor.forClass(View.OnClickListener.class);
+ verify(mView).setOnClickListener(clickListenerCaptor.capture());
+
+ when(mActivityIntentHelper.wouldShowOverLockscreen(mIntent, mCurrentUserId)).thenReturn(
+ false);
+
+ clickListenerCaptor.getValue().onClick(mView);
+ verify(mActivityStarter).postStartActivityDismissingKeyguard(mPendingIntent, null);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt
new file mode 100644
index 0000000..3a61c57
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BouncerCallbackInteractorTest : SysuiTestCase() {
+ private val bouncerCallbackInteractor = BouncerCallbackInteractor()
+ @Mock private lateinit var bouncerExpansionCallback: KeyguardBouncer.BouncerExpansionCallback
+ @Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ bouncerCallbackInteractor.addBouncerExpansionCallback(bouncerExpansionCallback)
+ bouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback)
+ }
+
+ @Test
+ fun testOnFullyShown() {
+ bouncerCallbackInteractor.dispatchFullyShown()
+ verify(bouncerExpansionCallback).onFullyShown()
+ }
+
+ @Test
+ fun testOnFullyHidden() {
+ bouncerCallbackInteractor.dispatchFullyHidden()
+ verify(bouncerExpansionCallback).onFullyHidden()
+ }
+
+ @Test
+ fun testOnExpansionChanged() {
+ bouncerCallbackInteractor.dispatchExpansionChanged(5f)
+ verify(bouncerExpansionCallback).onExpansionChanged(5f)
+ }
+
+ @Test
+ fun testOnVisibilityChanged() {
+ bouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE)
+ verify(bouncerExpansionCallback).onVisibilityChanged(false)
+ }
+
+ @Test
+ fun testOnStartingToHide() {
+ bouncerCallbackInteractor.dispatchStartingToHide()
+ verify(bouncerExpansionCallback).onStartingToHide()
+ }
+
+ @Test
+ fun testOnStartingToShow() {
+ bouncerCallbackInteractor.dispatchStartingToShow()
+ verify(bouncerExpansionCallback).onStartingToShow()
+ }
+
+ @Test
+ fun testOnKeyguardReset() {
+ bouncerCallbackInteractor.dispatchReset()
+ verify(keyguardResetCallback).onKeyguardReset()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
new file mode 100644
index 0000000..e6c8dd8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.DejankUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.utils.os.FakeHandler
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner::class)
+class BouncerInteractorTest : SysuiTestCase() {
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var repository: KeyguardBouncerRepository
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var bouncerView: BouncerView
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
+ @Mock private lateinit var bouncerCallbackInteractor: BouncerCallbackInteractor
+ @Mock private lateinit var falsingCollector: FalsingCollector
+ @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
+ @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ private val mainHandler = FakeHandler(Looper.getMainLooper())
+ private lateinit var bouncerInteractor: BouncerInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ DejankUtils.setImmediate(true)
+ bouncerInteractor =
+ BouncerInteractor(
+ repository,
+ bouncerView,
+ mainHandler,
+ keyguardStateController,
+ keyguardSecurityModel,
+ bouncerCallbackInteractor,
+ falsingCollector,
+ dismissCallbackRegistry,
+ keyguardBypassController,
+ keyguardUpdateMonitor,
+ )
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ `when`(repository.show.value).thenReturn(null)
+ }
+
+ @Test
+ fun testShow_isScrimmed() {
+ bouncerInteractor.show(true)
+ verify(repository).setShowMessage(null)
+ verify(repository).setOnScreenTurnedOff(false)
+ verify(repository).setKeyguardAuthenticated(null)
+ verify(repository).setHide(false)
+ verify(repository).setStartingToHide(false)
+ verify(repository).setScrimmed(true)
+ verify(repository).setExpansion(EXPANSION_VISIBLE)
+ verify(repository).setShowingSoon(true)
+ verify(keyguardStateController).notifyBouncerShowing(true)
+ verify(bouncerCallbackInteractor).dispatchStartingToShow()
+ verify(repository).setVisible(true)
+ verify(repository).setShow(any(KeyguardBouncerModel::class.java))
+ verify(repository).setShowingSoon(false)
+ }
+
+ @Test
+ fun testShow_isNotScrimmed() {
+ verify(repository, never()).setExpansion(EXPANSION_VISIBLE)
+ }
+
+ @Test
+ fun testShow_keyguardIsDone() {
+ `when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true)
+ verify(keyguardStateController, never()).notifyBouncerShowing(true)
+ verify(bouncerCallbackInteractor, never()).dispatchStartingToShow()
+ }
+
+ @Test
+ fun testHide() {
+ bouncerInteractor.hide()
+ verify(falsingCollector).onBouncerHidden()
+ verify(keyguardStateController).notifyBouncerShowing(false)
+ verify(repository).setShowingSoon(false)
+ verify(repository).setOnDismissAction(null)
+ verify(repository).setVisible(false)
+ verify(repository).setHide(true)
+ verify(repository).setShow(null)
+ }
+
+ @Test
+ fun testExpansion() {
+ `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ bouncerInteractor.setExpansion(0.6f)
+ verify(repository).setExpansion(0.6f)
+ verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
+ }
+
+ @Test
+ fun testExpansion_fullyShown() {
+ `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ bouncerInteractor.setExpansion(EXPANSION_VISIBLE)
+ verify(falsingCollector).onBouncerShown()
+ verify(bouncerCallbackInteractor).dispatchFullyShown()
+ }
+
+ @Test
+ fun testExpansion_fullyHidden() {
+ `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ bouncerInteractor.setExpansion(EXPANSION_HIDDEN)
+ verify(repository).setVisible(false)
+ verify(repository).setShow(null)
+ verify(falsingCollector).onBouncerHidden()
+ verify(bouncerCallbackInteractor).dispatchReset()
+ verify(bouncerCallbackInteractor).dispatchFullyHidden()
+ }
+
+ @Test
+ fun testExpansion_startingToHide() {
+ `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE)
+ bouncerInteractor.setExpansion(0.1f)
+ verify(repository).setStartingToHide(true)
+ verify(bouncerCallbackInteractor).dispatchStartingToHide()
+ }
+
+ @Test
+ fun testShowMessage() {
+ bouncerInteractor.showMessage("abc", null)
+ verify(repository).setShowMessage(BouncerShowMessageModel("abc", null))
+ }
+
+ @Test
+ fun testDismissAction() {
+ val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java)
+ val cancelAction = mock(Runnable::class.java)
+ bouncerInteractor.setDismissAction(onDismissAction, cancelAction)
+ verify(repository)
+ .setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction))
+ }
+
+ @Test
+ fun testUpdateResources() {
+ bouncerInteractor.updateResources()
+ verify(repository).setResourceUpdateRequests(true)
+ }
+
+ @Test
+ fun testNotifyKeyguardAuthenticated() {
+ bouncerInteractor.notifyKeyguardAuthenticated(true)
+ verify(repository).setKeyguardAuthenticated(true)
+ }
+
+ @Test
+ fun testOnScreenTurnedOff() {
+ bouncerInteractor.onScreenTurnedOff()
+ verify(repository).setOnScreenTurnedOff(true)
+ }
+
+ @Test
+ fun testSetKeyguardPosition() {
+ bouncerInteractor.setKeyguardPosition(0f)
+ verify(repository).setKeyguardPosition(0f)
+ }
+
+ @Test
+ fun testNotifyKeyguardAuthenticatedHandled() {
+ bouncerInteractor.notifyKeyguardAuthenticatedHandled()
+ verify(repository).setKeyguardAuthenticated(null)
+ }
+
+ @Test
+ fun testNotifyUpdatedResources() {
+ bouncerInteractor.notifyUpdatedResources()
+ verify(repository).setResourceUpdateRequests(false)
+ }
+
+ @Test
+ fun testSetBackButtonEnabled() {
+ bouncerInteractor.setBackButtonEnabled(true)
+ verify(repository).setIsBackButtonEnabled(true)
+ }
+
+ @Test
+ fun testStartDisappearAnimation() {
+ val runnable = mock(Runnable::class.java)
+ bouncerInteractor.startDisappearAnimation(runnable)
+ verify(repository).setStartDisappearAnimation(any(Runnable::class.java))
+ }
+
+ @Test
+ fun testIsFullShowing() {
+ `when`(repository.isVisible.value).thenReturn(true)
+ `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE)
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ assertThat(bouncerInteractor.isFullyShowing()).isTrue()
+ `when`(repository.isVisible.value).thenReturn(false)
+ assertThat(bouncerInteractor.isFullyShowing()).isFalse()
+ }
+
+ @Test
+ fun testIsScrimmed() {
+ `when`(repository.isScrimmed.value).thenReturn(true)
+ assertThat(bouncerInteractor.isScrimmed()).isTrue()
+ `when`(repository.isScrimmed.value).thenReturn(false)
+ assertThat(bouncerInteractor.isScrimmed()).isFalse()
+ }
+
+ @Test
+ fun testIsInTransit() {
+ `when`(repository.showingSoon.value).thenReturn(true)
+ assertThat(bouncerInteractor.isInTransit()).isTrue()
+ `when`(repository.showingSoon.value).thenReturn(false)
+ assertThat(bouncerInteractor.isInTransit()).isFalse()
+ `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ assertThat(bouncerInteractor.isInTransit()).isTrue()
+ }
+
+ @Test
+ fun testIsAnimatingAway() {
+ `when`(repository.startingDisappearAnimation.value).thenReturn(Runnable {})
+ assertThat(bouncerInteractor.isAnimatingAway()).isTrue()
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ assertThat(bouncerInteractor.isAnimatingAway()).isFalse()
+ }
+
+ @Test
+ fun testWillDismissWithAction() {
+ `when`(repository.onDismissAction.value?.onDismissAction)
+ .thenReturn(mock(ActivityStarter.OnDismissAction::class.java))
+ assertThat(bouncerInteractor.willDismissWithAction()).isTrue()
+ `when`(repository.onDismissAction.value?.onDismissAction).thenReturn(null)
+ assertThat(bouncerInteractor.willDismissWithAction()).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index 5dd1cfc..e3e3b74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.PendingIntent
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -43,6 +44,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
@@ -366,7 +368,7 @@
playerIndex,
mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex
)
- assertEquals( playerIndex, 0)
+ assertEquals(playerIndex, 0)
// Replaying the same media player one more time.
// And check that the card stays in its position.
@@ -402,4 +404,44 @@
visualStabilityCallback.value.onReorderingAllowed()
assertEquals(true, result)
}
+
+ @Test
+ fun testGetCurrentVisibleMediaContentIntent() {
+ val clickIntent1 = mock(PendingIntent::class.java)
+ val player1 = Triple("player1",
+ DATA.copy(clickIntent = clickIntent1),
+ 1000L)
+ clock.setCurrentTimeMillis(player1.third)
+ MediaPlayerData.addMediaPlayer(player1.first,
+ player1.second.copy(notificationKey = player1.first),
+ panel, clock, isSsReactivated = false)
+
+ assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent1)
+
+ val clickIntent2 = mock(PendingIntent::class.java)
+ val player2 = Triple("player2",
+ DATA.copy(clickIntent = clickIntent2),
+ 2000L)
+ clock.setCurrentTimeMillis(player2.third)
+ MediaPlayerData.addMediaPlayer(player2.first,
+ player2.second.copy(notificationKey = player2.first),
+ panel, clock, isSsReactivated = false)
+
+ // mediaCarouselScrollHandler.visibleMediaIndex is unchanged (= 0), and the new player is
+ // added to the front because it was active more recently.
+ assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
+
+ val clickIntent3 = mock(PendingIntent::class.java)
+ val player3 = Triple("player3",
+ DATA.copy(clickIntent = clickIntent3),
+ 500L)
+ clock.setCurrentTimeMillis(player3.third)
+ MediaPlayerData.addMediaPlayer(player3.first,
+ player3.second.copy(notificationKey = player3.first),
+ panel, clock, isSsReactivated = false)
+
+ // mediaCarouselScrollHandler.visibleMediaIndex is unchanged (= 0), and the new player is
+ // added to the end because it was active less recently.
+ assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 0bfc034..2f52950 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.media.dream;
-import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION;
+import static com.android.systemui.flags.Flags.DREAM_MEDIA_COMPLICATION;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
@@ -68,7 +68,7 @@
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(true);
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)).thenReturn(true);
}
@Test
@@ -137,7 +137,7 @@
@Test
public void testOnMediaDataLoaded_mediaComplicationDisabled_doesNotAddComplication() {
- when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(false);
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)).thenReturn(false);
final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 2adc389..481e4e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -21,12 +21,16 @@
import android.view.MotionEvent
import android.view.ViewGroup
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardHostViewController
import com.android.keyguard.LockIconViewController
+import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationShadeDepthController
@@ -51,9 +55,9 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
-@SmallTest
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var view: NotificationShadeWindowView
@@ -72,8 +76,12 @@
@Mock
private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
private lateinit var ambientState: AmbientState
@Mock
+ private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
+ @Mock
private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@Mock
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@@ -87,6 +95,10 @@
private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
@Mock
private lateinit var pulsingGestureListener: PulsingGestureListener
+ @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
+ @Mock lateinit var keyguardBouncerContainer: ViewGroup
+ @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
+ @Mock lateinit var keyguardHostViewController: KeyguardHostViewController
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler
@@ -97,7 +109,6 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(view.bottom).thenReturn(VIEW_BOTTOM)
-
underTest = NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
FalsingCollectorFake(),
@@ -115,7 +126,10 @@
notificationShadeWindowController,
keyguardUnlockAnimationController,
ambientState,
- pulsingGestureListener
+ pulsingGestureListener,
+ featureFlags,
+ keyguardBouncerViewModel,
+ keyguardBouncerComponentFactory
)
underTest.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 001bfee..4a7dec9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -33,11 +33,14 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.LockIconViewController;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -86,6 +89,9 @@
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private AmbientState mAmbientState;
@Mock private PulsingGestureListener mPulsingGestureListener;
+ @Mock private FeatureFlags mFeatureFlags;
+ @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel;
+ @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
mInteractionEventHandlerCaptor;
@@ -121,7 +127,10 @@
mNotificationShadeWindowController,
mKeyguardUnlockAnimationController,
mAmbientState,
- mPulsingGestureListener
+ mPulsingGestureListener,
+ mFeatureFlags,
+ mKeyguardBouncerViewModel,
+ mKeyguardBouncerComponentFactory
);
mController.setupExpandedStatusBar();
mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index a4453f8..ee4b9d9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -41,11 +41,17 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.data.BouncerView;
+import com.android.systemui.keyguard.data.BouncerViewDelegate;
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -101,6 +107,13 @@
@Mock private SysUIUnfoldComponent mSysUiUnfoldComponent;
@Mock private DreamOverlayStateController mDreamOverlayStateController;
@Mock private LatencyTracker mLatencyTracker;
+ @Mock private FeatureFlags mFeatureFlags;
+ @Mock private KeyguardSecurityModel mKeyguardSecurityModel;
+ @Mock private BouncerCallbackInteractor mBouncerCallbackInteractor;
+ @Mock private BouncerInteractor mBouncerInteractor;
+ @Mock private BouncerView mBouncerView;
+// @Mock private WeakReference<BouncerViewDelegate> mBouncerViewDelegateWeakReference;
+ @Mock private BouncerViewDelegate mBouncerViewDelegate;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
@@ -115,6 +128,8 @@
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
+ when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
+
mStatusBarKeyguardViewManager =
new StatusBarKeyguardViewManager(
getContext(),
@@ -133,7 +148,12 @@
mKeyguardMessageAreaFactory,
Optional.of(mSysUiUnfoldComponent),
() -> mShadeController,
- mLatencyTracker);
+ mLatencyTracker,
+ mKeyguardSecurityModel,
+ mFeatureFlags,
+ mBouncerCallbackInteractor,
+ mBouncerInteractor,
+ mBouncerView);
mStatusBarKeyguardViewManager.registerCentralSurfaces(
mCentralSurfaces,
mNotificationPanelView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
index 37c0f36..bf43238 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
@@ -34,14 +34,14 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@SmallTest
-class StatusBarUserSwitcherControllerTest : SysuiTestCase() {
+class StatusBarUserSwitcherControllerOldImplTest : SysuiTestCase() {
@Mock
private lateinit var tracker: StatusBarUserInfoTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
new file mode 100644
index 0000000..f304647
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.content.pm.UserInfo
+import android.graphics.Bitmap
+import android.os.UserHandle
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.lang.ref.WeakReference
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BaseUserSwitcherAdapterTest : SysuiTestCase() {
+
+ @Mock private lateinit var controller: UserSwitcherController
+
+ private lateinit var underTest: BaseUserSwitcherAdapter
+
+ private lateinit var users: ArrayList<UserRecord>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ users =
+ ArrayList(
+ listOf(
+ createUserRecord(
+ id = 0,
+ picture = mock(),
+ isSelected = true,
+ isGuest = false,
+ ),
+ createUserRecord(
+ id = 1,
+ picture = mock(),
+ isSelected = false,
+ isGuest = false,
+ ),
+ createUserRecord(
+ id = UserHandle.USER_NULL,
+ picture = null,
+ isSelected = false,
+ isGuest = true,
+ ),
+ )
+ )
+
+ whenever(controller.users).thenAnswer { users }
+
+ underTest =
+ object : BaseUserSwitcherAdapter(controller) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
+ return mock()
+ }
+ }
+ }
+
+ @Test
+ fun `Adds self to controller in constructor`() {
+ val captor = kotlinArgumentCaptor<WeakReference<BaseUserSwitcherAdapter>>()
+ verify(controller).addAdapter(captor.capture())
+
+ assertThat(captor.value.get()).isEqualTo(underTest)
+ }
+
+ @Test
+ fun count() {
+ assertThat(underTest.count).isEqualTo(users.size)
+ }
+
+ @Test
+ fun `count - ignores restricted users when device is locked`() {
+ whenever(controller.isKeyguardShowing).thenReturn(true)
+ users =
+ ArrayList(
+ listOf(
+ createUserRecord(
+ id = 0,
+ picture = mock(),
+ isSelected = true,
+ isGuest = false,
+ isRestricted = false,
+ ),
+ createUserRecord(
+ id = 1,
+ picture = mock(),
+ isSelected = false,
+ isGuest = false,
+ isRestricted = true, // this one will be ignored.
+ ),
+ createUserRecord(
+ id = UserHandle.USER_NULL,
+ picture = null,
+ isSelected = false,
+ isGuest = true,
+ ),
+ )
+ )
+ assertThat(underTest.count).isEqualTo(users.size - 1)
+ }
+
+ @Test
+ fun `count - does not ignore restricted users when device is not locked`() {
+ whenever(controller.isKeyguardShowing).thenReturn(false)
+ users =
+ ArrayList(
+ listOf(
+ createUserRecord(
+ id = 0,
+ picture = mock(),
+ isSelected = true,
+ isGuest = false,
+ isRestricted = false,
+ ),
+ createUserRecord(
+ id = 1,
+ picture = mock(),
+ isSelected = false,
+ isGuest = false,
+ isRestricted = true,
+ ),
+ createUserRecord(
+ id = UserHandle.USER_NULL,
+ picture = null,
+ isSelected = false,
+ isGuest = true,
+ ),
+ )
+ )
+ assertThat(underTest.count).isEqualTo(users.size)
+ }
+
+ @Test
+ fun getItem() {
+ assertThat((0 until underTest.count).map { position -> underTest.getItem(position) })
+ .isEqualTo(users)
+ }
+
+ @Test
+ fun getItemId() {
+ (0 until underTest.count).map { position ->
+ assertThat(underTest.getItemId(position)).isEqualTo(position)
+ }
+ }
+
+ @Test
+ fun onUserListItemClicked() {
+ val userRecord = users[users.size / 2]
+ val dialogShower: UserSwitchDialogController.DialogShower = mock()
+
+ underTest.onUserListItemClicked(userRecord, dialogShower)
+
+ verify(controller).onUserListItemClicked(userRecord, dialogShower)
+ }
+
+ @Test
+ fun `getName - non guest - returns real name`() {
+ val userRecord =
+ createUserRecord(
+ id = 1,
+ picture = mock(),
+ )
+
+ assertThat(underTest.getName(context, userRecord)).isEqualTo(userRecord.info?.name)
+ }
+
+ @Test
+ fun `getName - guest and selected - returns exit guest action name`() {
+ val expected = "Exit guest"
+ context.orCreateTestableResources.addOverride(
+ com.android.settingslib.R.string.guest_exit_quick_settings_button,
+ expected,
+ )
+
+ val userRecord =
+ createUserRecord(
+ id = 2,
+ picture = null,
+ isGuest = true,
+ isSelected = true,
+ )
+
+ assertThat(underTest.getName(context, userRecord)).isEqualTo(expected)
+ }
+
+ @Test
+ fun `getName - guest and not selected - returns enter guest action name`() {
+ val expected = "Guest"
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.string.guest_name,
+ expected,
+ )
+
+ val userRecord =
+ createUserRecord(
+ id = 2,
+ picture = null,
+ isGuest = true,
+ isSelected = false,
+ )
+
+ assertThat(underTest.getName(context, userRecord)).isEqualTo("Guest")
+ }
+
+ @Test
+ fun refresh() {
+ underTest.refresh()
+
+ verify(controller).refreshUsers(UserHandle.USER_NULL)
+ }
+
+ private fun createUserRecord(
+ id: Int,
+ picture: Bitmap? = null,
+ isSelected: Boolean = false,
+ isGuest: Boolean = false,
+ isAction: Boolean = false,
+ isRestricted: Boolean = false,
+ ): UserRecord {
+ return UserRecord(
+ info =
+ if (isAction) {
+ null
+ } else {
+ UserInfo(id, "name$id", 0)
+ },
+ picture = picture,
+ isCurrent = isSelected,
+ isGuest = isGuest,
+ isRestricted = isRestricted,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index b4f3987b..b86ca6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -38,9 +38,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -102,8 +102,7 @@
ViewUtils.attachView(view)
testableLooper.processAllMessages()
- `when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController)
- `when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true)
+ `when`(userSwitcherController.isKeyguardShowing).thenReturn(true)
`when`(keyguardStateController.isShowing).thenReturn(true)
`when`(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
keyguardQsUserSwitchController.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
index 8dcd4bb..76ecc1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
@@ -86,7 +86,7 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class UserSwitcherControllerTest : SysuiTestCase() {
+class UserSwitcherControllerOldImplTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var activityManager: IActivityManager
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@@ -118,7 +118,7 @@
private lateinit var longRunningExecutor: FakeExecutor
private lateinit var uiExecutor: FakeExecutor
private lateinit var uiEventLogger: UiEventLoggerFake
- private lateinit var userSwitcherController: UserSwitcherController
+ private lateinit var userSwitcherController: UserSwitcherControllerOldImpl
private lateinit var picture: Bitmap
private val ownerId = UserHandle.USER_SYSTEM
private val ownerInfo = UserInfo(ownerId, "Owner", null,
@@ -205,7 +205,8 @@
}
private fun setupController() {
- userSwitcherController = UserSwitcherController(
+ userSwitcherController =
+ UserSwitcherControllerOldImpl(
mContext,
activityManager,
userManager,
@@ -230,7 +231,8 @@
dumpManager,
dialogLaunchAnimator,
guestResumeSessionReceiver,
- guestResetOrExitSessionReceiver)
+ guestResetOrExitSessionReceiver
+ )
userSwitcherController.init(notificationShadeWindowView)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 6b466e1..6fec343 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -60,7 +60,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(controller.addUsersFromLockScreen).thenReturn(MutableStateFlow(false))
+ whenever(controller.isAddUsersFromLockScreenEnabled).thenReturn(MutableStateFlow(false))
whenever(controller.isGuestUserAutoCreated).thenReturn(false)
whenever(controller.isGuestUserResetting).thenReturn(false)
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index bde9c3d..a6e1a32 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -22,6 +22,7 @@
import static android.service.voice.HotwordDetectedResult.EXTRA_PROXIMITY_METERS;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE;
+import static android.service.voice.HotwordDetectionService.ENABLE_PROXIMITY_RESULT;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN;
import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS;
@@ -185,7 +186,7 @@
final int mUser;
final Context mContext;
- @Nullable final AttentionManagerInternal mAttentionManagerInternal;
+ @Nullable AttentionManagerInternal mAttentionManagerInternal = null;
final AttentionManagerInternal.ProximityUpdateCallbackInternal mProximityCallbackInternal =
this::setProximityMeters;
@@ -240,9 +241,11 @@
mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed);
mRemoteHotwordDetectionService = mServiceConnectionFactory.createLocked();
- mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class);
- if (mAttentionManagerInternal != null) {
- mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal);
+ if (ENABLE_PROXIMITY_RESULT) {
+ mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class);
+ if (mAttentionManagerInternal != null) {
+ mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal);
+ }
}
mLastRestartInstant = Instant.now();